Mixing Forms and Token Authentication in a single ASP.NET Application (the Details)

The scenario described in my last post works because of the design around HTTP modules in ASP.NET. Authentication related modules (like Forms authentication and WIF WS-Fed/Sessions) typically subscribe to three events in the pipeline – AuthenticateRequest/PostAuthenticateRequest for pre-processing and EndRequest for post-processing (like making redirects to a login page).

In the pre-processing stage it is the modules’ job to determine the identity of the client based on incoming HTTP details (like a header, cookie, form post) and set HttpContext.User and Thread.CurrentPrincipal. The actual page (in the ExecuteHandler event) “sees” the identity that the last module has set.

So in our case there are three modules in effect:

  • FormsAuthenticationModule (AuthenticateRequest, EndRequest)
  • WSFederationAuthenticationModule (AuthenticateRequest, PostAuthenticateRequest, EndRequest)
  • SessionAuthenticationModule (AuthenticateRequest, PostAuthenticateRequest)

So let’s have a look at the different scenario we have when mixing Forms auth and WS-Federation.

Anoymous request to unprotected resource
This is the easiest case. Since there is no WIF session cookie or a FormsAuth cookie, these modules do nothing. The WSFed module creates an anonymous ClaimsPrincipal and calls the registered ClaimsAuthenticationManager (if any) to transform it. The result (by default an anonymous ClaimsPrincipal) gets set.

 

Anonymous request to FormsAuth protected resource
This is the scenario where an anonymous user tries to access a FormsAuth protected resource for the first time. The principal is anonymous and before the page gets rendered, the Authorize attribute kicks in. The attribute determines that the user needs authentication and therefor sets a 401 status code and ends the request. Now execution jumps to the EndRequest event, where the FormsAuth module takes over. The module then converts the 401 to a redirect (302) to the forms login page.

If authentication is successful, the login page sets the FormsAuth cookie.

 

FormsAuth authenticated request to a FormsAuth protected resource
Now a FormsAuth cookie is present, which gets validated by the FormsAuth module. This cookie gets turned into a GenericPrincipal/FormsIdentity combination. The WS-Fed module turns the principal into a ClaimsPrincipal and calls the registered ClaimsAuthenticationManager. The outcome of that gets set on the context.

 

Anonymous request to STS protected resource
This time the anonymous user tries to access an STS protected resource (a controller decorated with the RequireTokenAuthentication attribute). The attribute determines that the user needs STS authentication by checking the authentication type on the current principal. If this is not Federation, the redirect to the STS will be made.

After successful authentication at the STS, the STS posts the token back to the application (using WS-Federation syntax).

 

Postback from STS authentication
After the postback, the WS-Fed module finds the token response and validates the contained token. If successful, the token gets transformed by the ClaimsAuthenticationManager, and the outcome is a) stored in a session cookie, and b) set on the context.

 

STS authenticated request to an STS protected resource
This time the WIF Session authentication module kicks in because it can find the previously issued session cookie. The module re-hydrates the ClaimsPrincipal from the cookie and sets it.

 

FormsAuth and STS authenticated request to a protected resource
This is kind of an odd case – e.g. the user first authenticated using Forms and after that using the STS. This time the FormsAuth module does its work, and then afterwards the session module stomps over the context with the session principal. In other words, the STS identity wins.

 

What about roles?
A common way to set roles in ASP.NET is to use the role manager feature. There is a corresponding HTTP module for that (RoleManagerModule) that handles PostAuthenticateRequest. Does this collide with the above combinations?

No it doesn’t! When the WS-Fed module turns existing principals into a ClaimsPrincipal (like it did with the FormsIdentity), it also checks for RolePrincipal (which is the principal type created by role manager), and turns the roles in role claims. Nice!

But as you can see in the last scenario above, this might result in unnecessary work, so I would rather recommend consolidating all role work (and other claims transformations) into the ClaimsAuthenticationManager. In there you can check for the authentication type of the incoming principal and act accordingly.

HTH

This entry was posted in IdentityModel. Bookmark the permalink.

5 Responses to Mixing Forms and Token Authentication in a single ASP.NET Application (the Details)

  1. Pingback: Creating a custom Login page for federated authentication with Windows Azure ACS | A Cloudy Place

  2. Fred Legrain says:

    Hi. Interesting post, thank you!
    I’m tying to make it work, but I couldn’t find the RequireTokenAuthenticationAttribute within WIF 4.0. What version were you working on for that article?

  3. RonyK says:

    Hi, I’m trying to figure out the best way to accomplish a guest visitor that is not authenticated by an sts or forms authentication but by a unique invitation id. The guest was sent an invite link with a unique id (related to the resources he is authorized for). Could the invitation link be to a resource that does not need authorization, then after checking the invitation id, is it possible to mimic the fam/sam action and issue some kind of IdentityPrincipal for the user? Allowing consequent calls to appear authenticated and contain claims to identify the user as “Guest” with “X” invitation id. I would need the appropriate cookies to be set as well, to allow authenticated Ajax calls as well.
    Am I going in the right direction? is something like that possible?

    • I think yes.

      In general on that target page, after validating the invitation id, you can create a ClaimsPrincipal and write that out as a session cookie – using SessionSecurityToken and the SAM.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s