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.
Pingback: Creating a custom Login page for federated authentication with Windows Azure ACS | A Cloudy Place
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?
I guess this is the one:
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.
We have a claim aware application created using Windows Identity foundation and working fine with ADFS. Now this application needs to be used both by Enterprise users and external users.
So we want to use windows integrated login(Windows authentication) in this application along with claim based authentication. We tried to set authentication type to windows in web.config, but application is not using it(ignoring the tag). I believe that is because WSFederationModule bypasses WindowsAuthentication Module.
If i am correct ,Is there some way by which i can enable windows authentication in a WIF application, so that the Enterprise user can seemlessly login using Windows Integrated authentication without any further prompt while external user can login using ADFS login page
ASP.NET is not designed to support multiple authentication types. You would use ADFS for both types of users.
If ADFS is configured correctly, the internal users will not see a login page but will do integrated authentication.
Yes you are correct because in ADFS config both form and windows authentication are mentioned and executed in their order of placement.
Our issue is that we have created a custom login page in our application(SP) which send username password to ADFS FormSignOnpage through query string.
So here is the catch that we are not using ADFS login page but our own login page. If we keep Windows authentication at first place in ADFS, even external users are prompted with windows username/password default popup which is not desired.
And if we keep form authentication in ADFS at first place, windows users which are not recongnised in our own login page(as it is claim aware application so windowsauthenticationmdoule is not working) are ultimatly served ADFS login page which is not desired.
So now we are trying to see if we can somehow catch in our Claim aware application that incoming user has windows credentials so that we can hit the ADFS with Wauth pararmeter as windows authentication.
If suggest, what should we do in this case.
Hope i am able to clarify my issue properly.