Windows Domain Authentication on Windows Phone 7

One of the projects that’s been kicking around in the back of my head is how to make Windows Phone 7 applications able to authenticate against a Windows domain.  This is a must have for enterprise developers if they want to use the new platform.

There were a couple ways I could do this, but keeping with my Claims-shtick I figured I would use an STS.  Given that ADFS is designed specifically for Active Directory authentication, I figured it would work nicely.  It should work like this:

image

Nothing too spectacularly interesting about the process.  In order to use ADFS though, I need the correct endpoint.  In this case I’m using

https://[external.exampledomain.com]/adfs/services/Trust/13/usernamemixed

That takes care of half of the problem.  Now I actually need to make my application call that web service endpoint. 

This is kind of a pain because WP7/Silverlight don’t support the underlying protocol, WS-Federation.

Theoretically I could just add that endpoint as a service reference and build up all the pieces, but that is a nightmare scenario because of all the boiler-plating around security.  It would be nice if there was a library that supported WS-Federation for the phone.

As it turns out Dominick Baier came across a solution.  He converted the project that came from the Identity training kit initially designed for Silverlight.  As he mentions there were a few gotchas, but overall it worked nicely.  You can download his source code and play around.

I decided to take it a step further though.  I didn’t really like the basic flow of token requests, and I didn’t like how I couldn’t work with IPrincipal/IIdentity objects.

First things first though.  I wanted to start from scratch, so I opened the identity training kit and looked for the Silverlight project.  You can find it here: [wherever you installed the kit]\IdentityTrainingKitVS2010\Labs\SilverlightAndIdentity\Source\Assets\SL.IdentityModel.

Initially I thought I could just add it to a phone project, but that was a bad idea; there were too many build errors.  I could convert the project file to a phone library, but frankly I was lazy, so I just created a new phone library and copied the source files between projects.

There were a couple references missing, so I added System.Runtime.Serialization, System.ServiceModel, and System.Xml.Linq.

This got the project built, but will it work?

I copied Dominick’s code:

WSTrustClient _client;

private void button1_Click(object sender, RoutedEventArgs e)
{
    _client = GetWSTrustClient(
https://[...]/adfs/services/Trust/13/usernamemixed,
new UsernameCredentials("username", "password")); var rst = new RequestSecurityToken(WSTrust13Constants.KeyTypes.Bearer) { AppliesTo = new EndpointAddress("[…]") }; _client.IssueCompleted += client_IssueCompleted; _client.IssueAsync(rst); } void client_IssueCompleted(object sender, IssueCompletedEventArgs e) { _client.IssueCompleted -= client_IssueCompleted; if (e.Error != null) throw e.Error; var token = e.Result; button2.IsEnabled = true; } private WSTrustClient
GetWSTrustClient(string stsEndpoint, IRequestCredentials credentials) { var client = new WSTrustClient(new WSTrustBindingUsernameMixed(),
new EndpointAddress(stsEndpoint), credentials); return client; }

To my surprise it worked.  Sweet.

This left me wanting more though.  In order to access any of the claims within the token I had to do something with the RequestSecurityTokenResponse (RSTR) object.  Also, how do I make this identity stick around within the application?

The next thing I decided to do was figure out how to convert the RSTR object to an IClaimsIdentity.  Unfortunately this requires a bit of XML parsing.  Talk about a pain.  Helper class it is:

public static class TokenHandler
{
    private static XNamespace ASSERTION_NAMESPACE 
= "urn:oasis:names:tc:SAML:1.0:assertion"; private const string CLAIM_VALUE_TYPE
= "http://www.w3.org/2001/XMLSchema#string"; // bit of a hack public static IClaimsPrincipal Convert(RequestSecurityTokenResponse rstr) { return new ClaimsPrincipal(GetClaimsIdentity(rstr)); } private static ClaimsIdentity GetClaimsIdentity(RequestSecurityTokenResponse rstr) { XDocument responseDoc = XDocument.Parse(rstr.RequestedSecurityToken.RawToken); XElement attStatement = responseDoc.Element(ASSERTION_NAMESPACE + "Assertion")
.Element(ASSERTION_NAMESPACE + "AttributeStatement"); var issuer = responseDoc.Root.Attribute("Issuer").Value; ClaimCollection claims = new ClaimCollection(); foreach (var c in attStatement.Elements(ASSERTION_NAMESPACE + "Attribute")) { string attrName = c.Attribute("AttributeName").Value; string attrNamespace = c.Attribute("AttributeNamespace").Value; string claimType = attrNamespace + "/" + attrName; foreach (var val in c.Elements(ASSERTION_NAMESPACE + "AttributeValue")) { claims.Add(new Claim(issuer, issuer, claimType,
val.Value, CLAIM_VALUE_TYPE)); } } return new ClaimsIdentity(claims); } }

Most of this is just breaking apart the SAML-goo.  Once I got all the SAML assertions I generated a claim for each one and created a ClaimsIdentity object.  This gets me a step closer to how I wanted things, but keeping the identity around within the application is still up in the air.  How can I keep the identity for the lifetime of the application?  I wanted something like Thread.CurrentPrincipal but the phone platform doesn’t let you access it.

There was a class, TokenCache, that was part of the original Silverlight project.  This sounded useful.  it turns out it’s Get/Add wrapper for a Dictionary<>.  It’s almost useful, but I want to be able to access this cache at any time.  A singleton sort of solves the problem, so lets try that.  I added this within the TokenCache class:

public static TokenCache Cache
{
    get
    {
        if (_cache != null)
            return _cache;

        lock (_sync)
        {
            _cache = new TokenCache();
        }

        return _cache;
    }
}

private static TokenCache _cache;
private static object _sync = new object();

 

now I can theoretically get access to the tokens at any time, but I want to make the access part of the base Application object.  I created a static class called ApplicationExtensions:

public static class ApplicationExtensions
{
    public static IClaimsPrincipal 
GetPrincipal(this Application app, string appliesTo) { if (!TokenCache.Cache.HasTokenInCache(appliesTo)) throw new ArgumentException("Token cannot be found to generate principal."); return TokenHandler.Convert(TokenCache.Cache.GetTokenFromCache(appliesTo)); } public static RequestSecurityTokenResponse
GetPrincipalToken(this Application app, string appliesTo) { return TokenCache.Cache.GetTokenFromCache(appliesTo); } public static void
SetPrincipal(this Application app, RequestSecurityTokenResponse rstr) { TokenCache.Cache.AddTokenToCache(rstr.AppliesTo.ToString(), rstr); } }

 

It adds three extension methods to the base Application object.  Now it’s sort of like Thread.CurrentPrincipal.

How does this work?  When the RSTR is returned I can call:

Application.Current.SetPrincipal(rstr);
 

Accessing the identity is two-part.

If I just want to get the identity and it’s claims I can call:

var principal = Application.Current.GetPrincipal("https://troymcclure/webapplication3/");

IClaimsIdentity ident = principal.Identity as IClaimsIdentity;
 

If I want to reuse the token as part of web service call I can get the token via:

var token = Application.Current.GetPrincipalToken(https://troymcclure/webapplication3/);
 

There is still quite a lot to do in order for this to be production ready code, but it does a pretty good job of solving all the problems I had with domain authentication on the Windows Phone 7 platform.