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;
}
}