Silverlight [WPF/E] and Windows CardSpace or plugging RIA in the Identity Metasystem

[Edit: Added Silverlight SxS con WPF/E] 

In short: this is a tutorial on invoking Cardspace from a Sliverlight [WPF/E] control and how to use Silverlight [WPF/E] for showing data from a token. So easy that a long haired architect can do it :-)
Silverlight [WPF/E] is Microsoft's technology for developing rich internet applications, but it is also going to be CROSS PLATFORM (the CTP it is already available for Mac). In light of the awesome work of the Bandit guys on an identity selector on other platforms, I believe it is important to start thinking about how to use this new RIA technology together with identity.

 Image claims rendered via WPFE

In recent times I'm hearing more and more people interested in Rich Internet Applications, or RIA. That usually brings the discussion pretty quickly on Silverlight [WPF/E], our cross platform presentation technology that leverages a subset of XAML for doing cool things inside your browser. I am often asked how to plug CardSpace into it, so I thought to put toghether a post that shows how to do that.
As you know it's few years that I am a server guy, so I don't spend too much time on colorful stuff: however I also like to cross pollinate different technologies, and I especially love to do it with CardSpace (I did it with WPF, with WF, with WCF and WPF). Yesterday night I downloaded the WPF/E SDK, the WPF/E runtime for Windows and blocked 1 hour on the calendar of my excellent colleague Laurence Moroney, probably the best mentor I could get for ramping up super fast on this technology. Thank you man!!!!

My objective was to use that hour for coming out with a full Silverlight [WPF/E]+CardSpace PoC, namely: 1) I wanted to invoke the identity selector from a Silverlight [WPF/E] UI 2) I wanted to render some info pulled out from a token. As you can see, there's no buisiness scenario involved: this is pure syntactic sugar, how to get the two to work together. As I hoped, we declared mission accomplished after just 35 minutes :-) below the details.

The steps are

  1. Install the Silverlight [WPF/E] necessaire
  2. Familiarize with the basic Silverlight [WPF/E] project
  3. Modify a cardspace login page for using a Silverlight [WPF/E] control to invoke the Identity Selector
  4. Modify an ASPX page for showing some claims values using Silverlight [WPF/E]

Install the Silverlight [WPF/E] necessaire

If you are new to Silverlight [WPF/E]: you can get all the details here. However, for the purpose of our discussion: Silverlight [WPF/E] is a subset of XAML, the markup language behind WPF, that allows you to deliver in the browser interactive applications containing animation, audio, video, graphic, text and obviously interactivity. The interesting point is that Silverlight [WPF/E] works CROSS PLATFORM, that is to say that your Silverlight [WPF/E] application is available on multiple browsers and multiple platforms (WIndows and Mac). In practice, there will be a lightweight plugin that will enable the browser to understand and render XAML code. Neat!!!
The most recent build is the February CTP. You can get the runtime from here, while the SDK is here. If you use VIsual Studio 2005, after the setup go in the SDK menu folder: there you will find an entry for installing a Visual Studio WPF/E project template, I suggest installing it: it makes things easier for this tutorial.
Note: if you want to nurture your inner designer, Expression Blend is the ideal tool for the task. I am not considering it in this post, since I suspect we are all thick skinned protocol guys around here :-)

 Familiarize with the basic Silverlight [WPF/E] project

In order to avoid wasting Laurence's time, yesterday night I familiarized with the basic Silverlight [WPF/E] project.
If you installed the VS template, go ahead and create a new Silverlight [WPF/E] project. Don't change anything from the defaults. The solution explorer ends up with the following structure:

The file plugin.xaml contains the UI of our control. The syntax is very straightfoward: this is a canvas called button, containing a rectangle and the text "Click Me". There's no binary black magic, this is just plain old human readable XML:

Canvas xmlns="https://schemas.microsoft.com/client/2007"

        xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"

        Loaded="javascript:root_Loaded">

  <Canvas x:Name="button">

    <Rectangle Stroke="#FF8E8E8E" StrokeThickness="2" RadiusX="2" RadiusY="2" Height="23" Width="75">

      <Rectangle.Fill>

        <LinearGradientBrush StartPoint="0.5,2.109" EndPoint="0.5,-1.109">

          <GradientStop x:Name="gradientStop1" Color="#FFFF9E00" Offset="1"/>

          <GradientStop x:Name="gradientStop2" Color="#FFEAEAEA" Offset="0.218"/>

        </LinearGradientBrush>

      </Rectangle.Fill>

    </Rectangle>

    <TextBlock Canvas.Top="3" Canvas.Left="13" FontSize="12" Foreground="#FF5A5A5A" Text="Click Me" />

  </Canvas>

</Canvas>

The file Default.html is the only page of our site. The code is, again, straightforward:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="https://www.w3.org/1999/xhtml" >

<head>

    <title>Untitled Page</title>

    <script type="text/javascript" src="js/aghost.js"></script>

    <script type="text/javascript" src="js/eventhandlers.js"></script>

</head>

<body>

    <form>

        <div id="wpfeControl1Host" >

            <script type="text/javascript">

       new agHost("wpfeControl1Host", // hostElementID (HTML element to put WPF/E control into)

       "wpfeControl1", // ID of the WPF/E ActiveX control we create

       "400", // Width

       "400", // Height

       "white", // Background color

       null, // SourceElement (name of script tag containing xaml)

       "plugin.xaml", // Source file

       "false", // IsWindowless

       "30", // MaxFrameRate

       null, // OnError handler (method name -- no quotes)

                   0, // Minimum major version required

  8, // Minimum minor version required

                   5 // Minimum build required

       )

            </script>

        </div>

    </form>

</body>

</html>

 

The head contains two references (highlighted in green) to javascript files, the last items files of the solution: we'll see their details shortly.
The body contains a simple form, with a named div and a single call to function agHost. It's not hard to imagine the purpose of the function call: we use it for instantiating in the page our Silverlight [WPF/E] UI. You can notice in the parameters plugin.xaml, the name of the first file we examined in the solution.

We are left with the JS files.
I won't go into the details of aghost.js, nor should you: its function is well explained by the file header, that I am copying below.

///////////////////////////////////////////////////////////////////////////////

//

// aghost.js

//

// February 2007 Community Technology Preview

//

// This file is provided by Microsoft as a helper file for websites that

// incorporate pre-release WPF/E objects. The 1.29.2007 version of the file

// is configured to check for WPF/E version 0.8.5.0. You may modify this file

// for testing purposes.

//

// copyright 2007, Microsoft Corporation

//

///////////////////////////////////////////////////////////////////////////////

The content of the last file, eventhandlers.js, is coherent with its name. It is a collection of javascript functions modeling the behaviors of the Silverlight [WPF/E] control:

function root_Loaded(sender, args) {

    var button = sender.findName("button");

    button.mouseEnter = "javascript:handleMouseEnter";

    button.mouseLeave = "javascript:handleMouseLeave";

    button.mouseLeftButtonUp = "javascript:handleMouseUp";

    button.mouseLeftButtonDown = "javascript:handleMouseDown";

}

function handleMouseEnter(sender, eventArgs) {

    var gradientStop1 = sender.findName("gradientStop1");

       var gradientStop2 = sender.findName("gradientStop2");

       gradientStop1.offset = 1;

       gradientStop2.offset = .403;

}

...

function handleMouseUp(sender, eventArgs) {

       var gradientStop1 = sender.findName("gradientStop1");

       var gradientStop2 = sender.findName("gradientStop2");

       gradientStop1.offset = 1;

       gradientStop2.offset = .403;

      

       alert("clicked");

}

...

 

The function root_loader finds our control, and wires up the events. HandleMouseEnter and other functions (where you see the ellipsis, I have snipped a function away) do some graphic legwork. Again, notice how easy it is to manipulate XAML directly from javascript. What interests us is the line highlighted in yellow: that's the piece of code we want to execute when the user clicks (or better, mouse-ups) the control.

If you hit F5, you'll see a page with a single button that shows an alert when clicked.

 

"Doh", some of you will say. Well, take into account that we didn't change a tad of the template project: I wanted to understand how Silverlight [WPF/E] works, and for that we have to keep the signal/noise ratio to a mangeable value. You are of course invited to make things prettier :-)

Summary. How does a Silverlight [WPF/E] project works? You create your UI in XAML, you instantiate it in an HTML page (via aghost from the aghost.js file) and you handle interactivity via usual javascript events wiring. So easy that a long haired architect can do it.

Modify a Cardspace login page for using a Silverlight [WPF/E] control to invoke the Identity Selector

Now we get to the interesting part. It is all very easy: however, if you are not familiar with the basic CardSpace samples from this point on it will be difficult to follow. I recommend you to download and play with those samples, since you'll have to modify them with what we have just learned about Silverlight [WPF/E]. They are very clear and straighforward, in true Garrett style: from my good friend and book buddy, I would not expect anything less :-)

 Let's get to work. Open your favourite Cardspace sample solution (if you use those samples, I would suggest exercise 3 or 4). What we want to obtain is very easy: we have to elicit the form post from our Silverlight [WPF/E] button instead of the usual HTLM one.
First thing, we copy all the necessaire in the cardspace solution (or in the folder, if you are not using visual studio). The necessaire in this case is the JS directory, with aghost.js and eventhandles, and the XAML file (which for the occasion I renamed LoginControl.xaml).
Once we have copied those, we have to modify the cardspace login page by 1) getting rid of the current mean of POSTing the form (usually a button with type submit) and 2) pasting in the XAML hosting code we have seen above. We will worry about modifying the logic wired to the Silverlight [WPF/E] control in the next steps.

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="https://www.w3.org/1999/xhtml" >

<head>    <title>Cardspace Login with WPF/e</title>

  <script type="text/javascript" src="js/aghost.js"></script>

  <script type="text/javascript" src="js/eventhandlers.js"></script>

</head>

<body>

    <form id="form1" name="form1" method="post" action="WPFeClaims.aspx">

        <div style="text-align: center">

            <object type="application/x-informationcard" name="xmlToken">

            <param name="tokenType" value="urn:oasis:names:tc:SAML:1.0:assertion" />

            <param name="issuer" value="https://schemas.xmlsoap.org/ws/2005/05/identity/issuer/self" />

            <param name="requiredClaims" value="https://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname https://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname https://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress https://schemas.xmlsoap.org/ws/2005/05/identity/claims/privatepersonalidentifier" />

            </object>

        </div>

        <div id="wpfeControl1Host" >

            <script type="text/javascript">

       new agHost("wpfeControl1Host", // hostElementID (HTML element to put WPF/E control into)

       "wpfeControl1", // ID of the WPF/E ActiveX control we create

       "400", // Width

       "400", // Height

       "white", // Background color

       null, // SourceElement (name of script tag containing xaml)

       "LoginControl.xaml", // Source file

       "false", // IsWindowless

       "30", // MaxFrameRate

       null, // OnError handler (method name -- no quotes)

                   0, // Minimum major version required

                   8, // Minimum minor version required

                   5 // Minimum build required

       )

            </script>

        </div>

    </form>

</body>

</html>

 Above you can see the modified HTML page. The green and yellow highloghts are the same fragments we had in the pure Silverlight [WPF/E] sample, apart from the name of the XAML file.
The light blue highlight shows the usual cardspace object tag, in the usual form. The form method is submit, as always; we POST to a known page, that we will craft in the last part of the tutorial.
In this moment, the HTML page does not contain anymore an element that could trigger the POST of the form. We are going to add that capability to the Silverlight [WPF/E] UI by modifying the code in eventhandlers.js:   

 

...

function handleMouseUp(sender, eventArgs) {

       var gradientStop1 = sender.findName("gradientStop1");

       var gradientStop2 = sender.findName("gradientStop2");

       gradientStop1.offset = 1;

       gradientStop2.offset = .403;

      

      

       document.form1.submit();

}

...

Here we show just the function handleMouseUp. Instead of the alert, we force the submit of the form via javascript. Again, veeeery simple. If I browse to the newly modified page and I click on the Silverlight [WPF/E] button I now get the dientity selector!

 

So, we accomplished our first task. We invoked the selector from Silverlight [WPF/E].  

Modify an ASPX page for showing some claims values using Silverlight [WPF/E]

This part is slightly trickier, but still very easy. What we want to prove is that it is possible to use Silverlight [WPF/E] for rendering data extracted from the security token we receive from CardSpace.
The XAML code is, again, just plain XML; as such, we can manipulate it as we like. Server side or Client side, it does not matter. For the purpose of the example, and for the sake of clarity, I have chosen the method that is the simplest to understand: do not take this as best practice.

In practically all web based CardSpace samples, we make use of the helper classes contained in TokenProcessor.cs. Thanks to that code, executed on the server by an ASPX page, we can deserialize and decrypt a security token for accessing the claim values. Hence, all we have to do is taking those values and injecting them in the XAML code that will render them. The operation is made even easier by a nice property of the XAML code: it can be inlined in the page. That is to say, we can generate the XAML directly from the server code with the values we need. We have just to use a slightly different syntax when we invoke the aghost function.
Below I paste the code of the page that applies considerations just made: don't get scared by its lenght, I will re-explain the concepts above function bu function by referring to the various colored regions of the source.

<%@ Page Language="C#" Debug="true" ValidateRequest="false" %>

<%@ Import Namespace="System.IdentityModel.Claims" %>

<%@ Import Namespace="Microsoft.IdentityModel.TokenProcessor" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<script runat="server">

    string stringvar =

@"<Canvas

    xmlns='https://schemas.microsoft.com/winfx/2006/xaml/presentation'

    xmlns:x='https://schemas.microsoft.com/winfx/2006/xaml'

    Width='400' Height='400'

    Background='White'

    >

    <TextBlock x:Name='str1' Width='200' Height='32' Canvas.Left='8' Canvas.Top='8' Text='String 1' TextWrapping='Wrap' FontSize='18' />

    <TextBlock x:Name='str2' Width='200' Height='32' Canvas.Left='130' Canvas.Top='8' Text='String 2' TextWrapping='Wrap' RenderTransformOrigin='0.5,0.5' FontSize='48' >

        <TextBlock.RenderTransform>

            <TransformGroup>

                  <RotateTransform Angle='90.014'/>

            </TransformGroup>

        </TextBlock.RenderTransform>

</TextBlock>

...

   

</Canvas>

";

   

    protected void Page_Load(object sender, EventArgs e)

    {

        string xmlToken;

        xmlToken = Request.Params["xmlToken"];

        if (xmlToken == null || xmlToken.Equals(""))

  {

            ShowError("Token presented was null");

        }

        else

        {

            Token token= new Token(xmlToken);

            stringvar = stringvar.Replace("String 1", token.Claims[ClaimTypes.GivenName]);

            stringvar = stringvar.Replace("String 2", token.Claims[ClaimTypes.Surname]);

            stringvar = stringvar.Replace("String 3", token.Claims[ClaimTypes.Email]);

            stringvar = stringvar.Replace("String 4", token.Claims[ClaimTypes.PPID]);

            stringvar = stringvar.Replace("String 5", token.UniqueID);

        }

    }

</script>

<html xmlns="https://www.w3.org/1999/xhtml" >

<head id="Head1" runat="server">

    <title>Login Page</title>

    <script type="text/javascript" src="js/aghost.js"></script>

    <script type="text/xaml" id="WPFeClaims"><% Response.Write(stringvar); %>

    </script>

</head>

<body>

    <form id="form1" runat="server">

       

<div id="wpfeControl1Host" >

    <script type="text/javascript">

        new agHost("wpfeControl1Host", // hostElementID (HTML element to put WPF/E control into)

                   "wpfeControl1", // ID of the WPF/E ActiveX control we create

                   "400", // Width

       "400", // Height

                   "white", // Background color

                   "WPFeClaims", // SourceElement (name of script tag containing xaml)

                   null, // Source file

         "false", // IsWindowless

                   "30", // MaxFrameRate

                   null, // OnError handler (method name -- no quotes)

       0, // Minimum major version required

       8, // Minimum minor version required

       5 // Minimum build required

                  )

    </script>

   </div>

    </form>

</body>

</html>

 

The top of the page contains the usual <%@ directives for importing the needed assemblies. We need IdentityModel and the TokenProcessor.

The turquoise area is a string, called stringvar, containing XAML code. It is cut short for clarity, however it contains a bunch of labels all rotate at different angles. The text ('String 1', 'String 2'...) is at this point just a placemark for the claim values.

The red area contains the classical TokenProcessor code: we get the content of the token that was POSTed to us, and we deserialize it in the Token class. Once we have a Token instance, we extract the claim values and we use them for replacing the palceholders in the variable containing the XAML code.

The we have the actual HTML page. In the header we have the usual import of aghost.js, in green. The really interesting part is the pink text: we have a script, of MIME type text/xaml, which contains the rendering of our xaml code. The script has id WPFeClaims.

Finally, the yellow part is the XAML instantiation. Notice that the SourceElement parameter refers to WPFeClaims, while the Source file parameter is now null.

That's it, this is all we have to do! If we go ahead and we send a card, the result looks similar to the image below:

 

Again, please remember that the images above do not give justice to the potential of Silverlight [WPF/E]: I just made a dull PoC.
As I hope the above demonstrated, integrating Silverlight [WPF/E] and Cardspace is very easy: so, if you want to make a RIA you don't have any more excuses: remember to add CardSpace support and make your application a good citizen of the Identity Metasystem! :-)

 

PS: I know, an example would have been handier. However the tutorial is a good forcing function for really understanding how it works, since you can't skip directly to the solution. However, if you want me to package the above in an example please let me know: if I receive enough requests I'll definitely do it.