Monday, July 18, 2011

WIF and custom UserData in authentication cookies

The UserData is a common and yet handy mechanism of authentication frameworks and it consist in injecting a portion of arbitrary data to the authentication information upon logon so that the data is available later during the session.

For example, FormsAuthenticationModule allows to inject userdata to the Forms cookie by manually creating the FormsAuthenticationTicket:

FormsAuthenticationTicket ticket = 
    new FormsAuthenticationTicket(
        1,
        UserName,
        DateTime.Now, DateTime.Now.AddMinutes( 20 ),
        false, "userdata here" );
string cookieValue = FormsAuthentication.Encrypt( ticket );

The similar mechanism in WIF is more “WS-Federation oriented” – you just create a new claim (one or many) of type ClaimTypes.UserData with arbitrary value. How exactly you create such claim? Well, there are two alternatives.

First, you can inject a custom claims authentication manager into the WIF’s pipeline. The manager fires at the beginning of every request and this is where you can inject “per-request” claims.

public class FederatedClaimsAuthenticationManager : ClaimsAuthenticationManager
{
    public override IClaimsPrincipal Authenticate( 
        string resourceName, 
        IClaimsPrincipal incomingPrincipal )
    {
        // If not authenticated - redirect
        if ( !incomingPrincipal.Identity.IsAuthenticated )
            return incomingPrincipal;
 
        // else - inject local claims
        IClaimsIdentity identity = incomingPrincipal.Identity as IClaimsIdentity;
        if ( identity != null && identity.IsAuthenticated )
        {
            identity.Claims.Add( new Claim( ClaimTypes.UserData, 
               "local per-request userdata", ClaimValueTypes.String, "(local)" ) );
        }
 
        return incomingPrincipal;
    }
}

Remember to register the manager in web.config in WIF’s <claimsAuthenticationManager /> section.

But injecting claims at every request is not a choice if you wish to inject userdata once, when user logs in and then be able to read it back at every request.

This is where the second possibility comes in. It consist in injecting the claim during the SessionSecurityTokenCreated event of the WIF’s SessionAuthenticationModule. Since I prefer the FederatedPassiveSignIn user control than direct fedutil.exe approach, I just provide a handler for the user control:

WSFederationLogin.SessionSecurityTokenCreated +=
     ( s, ev ) =>
     {
         ev.SessionToken
            .ClaimsPrincipal.Identities[0]
            .Claims.Add( 
                new Claim( ClaimTypes.UserData, 
                          "local per-session userdata (injected once)", 
                           ClaimValueTypes.String, "(local)" ) );
     };

In both approaches, the UserData claim is present among all other user claims.