4
votes

I have been working on a web site (using ASP.NET C#) that uses both forms based and claims based authentication. I wanted to override the ClaimsIdentity class so I could implement a custom IsAuthenticated method and add more properties for the identity specific for the claims authentication.

I'm implementing a custom WSFederationAuthentionModule currently, but I was trying to figure out what module I should override (specifically what method) so I can set my custom identity/principal rather than the default ClaimsPrincipal?

So far I have looked at both the SessionAuthenticationModule and ClaimsPrincipalHTTPModule, but I could not figure out at what step the principal is set/the best way to override it.

Thanks

Addition: Since I'm kind of new at this let me be sure this is correct: The way to set an identity is to set a custom principal which is set to use that identity:

System.Threading.Thread.CurrentPrincipal = customClaimsPrincipal;

Alternatively if a custom principal was not needed then the ClaimPrincipal class could be constructed with a ClaimsIdentityCollection.

2
Why would you not do this in the Application_AuthenticateRequest event in Global.asax? (I've not used claims authentication, but have used forms and written custom IIdentity etc..)jwwishart
I didn't want to do this because I want to only change the Identity when claims authentication is used. Also, I'm not particularly fond of putting things in the Global.asax because I need to keep code within HttpModules for extendability for other sites that are going to have to do the same thing. I'm new to this and not entirely positive, but it is my understanding that I should make sure that the other principle that is set (by SessionAuthentionModule, I think) is not set and only my custom principle/identity exist.kachingy123

2 Answers

3
votes

There are several places you can do this, but if you are using the SessionAuthenticationModule, some of the process is not documented well which can make using a custom principal tricky. The rest of this answer explains one possible way to handle this when using the SessionAuthenticationModule.

Override the SessionAuthenticationModule.SetPrincipalFromSessionToken method.

The SessionAuthenticationModule stores the security token, principal, identities, and claims in a cookie and an in-memory cache to avoid having to make round-trips to the identity provider/token service on every request. What's not documented well is the existence of the cache and that it is the first place checked, then the cookie, and the limits on serializing the ClaimsPrincipal.

If you already set a custom principal in ClaimsAuthenticationManager.Authenticate and the cache is intact, your custom principal will most likely be there already since the cache stores native .NET objects. If you haven't yet set a custom principal, or the cache is not populated, then the security token will be retrieved from the FedAuth session cookie.

When the token is serialized/deserialized to/from the cookie, the process uses custom serialization that is only capable of reading and writing attributes of the IClaimsPrincipal and IClaimsIdentity interfaces (or the ClaimsPrinicpal and ClaimsIdentity classes - I can't remember which). Any custom principal and identity object attributes will not be included. It may be possible to override the serialization, but that requires several (3 IIRC) more layers of class overrides.

You'll also need to be aware of the fact that the base SetPrincipalFromSessionToken method creates a new ClaimsPrincipal object and sets it on the thread and context, so even if the sessionSecurityToken parameter contains a custom principal object, it will get translated back into a ClaimsPrincipal object.

Here's an example override method:

protected override void SetPrincipalFromSessionToken(SessionSecurityToken sessionSecurityToken)
{
    SessionSecurityToken newToken = MyClaimsPrincipalUtility.CreateCustomClaimsPrincipalToken(sessionSecurityToken);

    base.SetPrincipalFromSessionToken(newToken);

    //the following lines need to be set after the base method call, because the base method overwrites the Principal object
    HttpContext.Current.User = newToken.ClaimsPrincipal;
    Thread.CurrentPrincipal = newToken.ClaimsPrincipal;
}

The base class (SessionAuthenticationModule) implementation looks like the following. So there are a couple different ways you can achieve the overriding and getting the custom claims principal.

protected virtual void SetPrincipalFromSessionToken(SessionSecurityToken sessionSecurityToken)
{
    IClaimsPrincipal fromIdentities = ClaimsPrincipal.CreateFromIdentities(this.ValidateSessionToken(sessionSecurityToken));

    HttpContext.Current.User = (IPrincipal) fromIdentities;
    Thread.CurrentPrincipal = (IPrincipal) fromIdentities;

    //tracing code removed

    this.ContextSessionSecurityToken = sessionSecurityToken;
}

And just in case you're wondering, here's the base class implementation for SessionAuthenticationModule.ContextSessionSecurityToken.

public virtual SessionSecurityToken ContextSessionSecurityToken
{
    get
    {
        return (SessionSecurityToken) HttpContext.Current.Items[(object) typeof (SessionSecurityToken).AssemblyQualifiedName];
    }
    internal set
    {
        HttpContext.Current.Items[(object) typeof (SessionSecurityToken).AssemblyQualifiedName] = (object) value;
    }
}
1
votes

You're correct in that HttpModules are the way to go in terms of extensibility, but make sure that whatever logic you implement doesn't inhibit the performance of your application. I've had websites go thoroughly berserk performance-wise after adding too many HttpModules to the application. It might be worth caching results of your queries, depending on what auth model you're using.

You need to figure out under what conditions you need to use your custom ClaimsPrincipal. Until you do this you're stabbing in the dark. Are there specific URLs for which you need claims authentication?