I think most of us .NET developers have used roles to authorize users in the apps they’ve built. While roles are fine, setting up fine grained access control to resources in an app can make the number of roles in an app explode. This is where claims step in to save the day! In the system I’m currently working we started with a mixed bag of roles (manager, admin, that sort of stuff) and custom authorisation that relies on things we dig up in a database (like roles, but a little bit more complex and not very extensible).
The poor extensiblity is where a decision was made to rewrite the entiry security system and implement a claims based authorisation system.
For this particular app we are only interested in the claims authorisation and not the entire WIF stack with Single Sign on support. The old-skool forms based authentication stays in place (using a MembershipProvider) but instead of a FormsAuthentication ticket we issue a SecuritySessionToken in a cookie that contains the claims. Lucky for us, WIF was integrated into the .NET Framework starting with version 4.5 so we don’t need external stuff for this to work.
Lets start with adding the WIF SessionAuthenticationModule to the pipeline so any security tokens we create get picked up and transformed into a ClaimsPrincipal instead of a GenericPrincipal for each request. Open web.config and add the module:
<modules> <add name="SessionAuthenticationModule" type="System.IdentityModel.Services.SessionAuthenticationModule, System.IdentityModel.Services, Version=184.108.40.206, Culture=neutral, PublicKeyToken=b77a5c561934e089"/> </modules>
Next thing to do is start creating tokens instead of forms tickets. This code replaces the code that would normaly create a forms authentication ticket.
var id = new ClaimsIdentity(new GenericIdentity(userName), claims, "Forms", string.Empty, string.Empty); var cp = new ClaimsPrincipal(id); var authedCp = FederatedAuthentication.FederationConfiguration.IdentityConfiguration.ClaimsAuthenticationManager.Authenticate("A_RESOURCE_NAME_HERE", cp); var token = FederatedAuthentication.SessionAuthenticationModule.CreateSessionSecurityToken(authedCp, "TOKEN_ISSUER", DateTime.UtcNow, DateTime.UtcNow.AddHours(1), false); // FederatedAuthentication.SessionAuthenticationModule.CookieHandler.RequireSsl = true; FederatedAuthentication.SessionAuthenticationModule.CookieHandler.RequireSsl = false; FederatedAuthentication.SessionAuthenticationModule.AuthenticateSessionSecurityToken(token, true);
Setting up a token requires a whole slew of objects, here is the breakdown:
Start with a new ClaimsIdentity and load it up with at least the username (in the generic identity) and a set of claims. Take the new ClaimsIdentity and stick it in a ClaimsPrincipal.
The next step is authenticating that new principal which creates a new principal that is authorized (authedCp). Set a proper name in the A_RESOURCE_NAME_HERE parameter. The name is of great importance here, we are the creator and issuer of the token. I put the name of our app there, which is the resource that the user is authorized for.
With the newly created authorized principal, we can create the token that represents our principal, the ISSUER_NAME in this case is you, so you can put the name of your app there.
You can choose to use SSL or not (of course you do! Wrap that in an #if RELEASE or something to not forget to set it to true before going to production ;-)).
For the final step we authenticate our token one more time and a cookie should now be set containing the claims.
The module we registered earlier will take care of reading the cookie for us and update the principal on each request. The new principal will be a ClaimsPrincipal and its claims collection will hold all the claims you provided when you created the token.
To check a claim you can use the ClaimsPrincipalPermission class and use its CheckAccess method to check if the current user is holding a specific claim for a resource and action.