Reputation: 1412
An existing web application I want to migrate to the Windows Azure Cloud authenticates users the following way somewhere in the (post)authenticaterequest event:
IPrincipal current = Thread.CurrentPrincipal;
if (current != null && ((IClaimsIdentity)current.Identity).Claims.Count > 0)
{
IPrincipal result = AuthManager.CreateGenericPrincipal(current.Identity);
HttpContext.Current.User = result;
Thread.CurrentPrincipal = result;
}
The CreateGenericPrincipal method looks up roles in a xml file for the claimsidentity and creates a new GenericPrincipal with that roles. Pages that need authentication just perform
IPrincipal p = Thread.CurrentPrincipal;
p.IsInRole("rolesFromXml");
This works fine with one webrole instance since there is no big difference to normal IIS hosting. But will it still work with 2, 3 oder 5 instances? The Azure loadbalancer is not "sticky", users could be forwarded to another instance while using the application. Dunno if Thread.CurrentPrincipal is still the way to go.
I use claims-based identity here. The first time an user enters the page, he gets forwarded to a security token service. Until now, this only happens once. It would be annoying if that happens several times when using multiple instances..
Thanks!
Upvotes: 2
Views: 1820
Reputation: 6868
What typically happens is that you are forwarded only once, the redirect dance (passive redirect) happens, and you get a token. The token is typically cached in a cookie in an encrypted format. So, subsequent requests do not do the redirect dance.
The challenge here is that since the cookie is encrypted, all servers in a web farm must have the encryption key to decrypt. Out of box, you will run into issues with WIF because it defaults to DPAPI. This type of encryption is intentionally different per machine. That breaks in the cloud.
What you need to do is upload a service certificate as part of your deployment and change the way the cookie encrypted to something that is webfarm friendly. Here is the magical code:
private void OnServiceConfigurationCreated(object sender,
ServiceConfigurationCreatedEventArgs e)
{
var sessionTransforms =
new List<CookieTransform>(
new CookieTransform[]
{
new DeflateCookieTransform(),
new RsaEncryptionCookieTransform(
e.ServiceConfiguration.ServiceCertificate),
new RsaSignatureCookieTransform(
e.ServiceConfiguration.ServiceCertificate)
});
var sessionHandler = new
SessionSecurityTokenHandler(sessionTransforms.AsReadOnly());
e.ServiceConfiguration.SecurityTokenHandlers.AddOrReplace(
sessionHandler);
}
This sets up your security token handler to use RSA Encryption with key material derived from the installed certificate.
There is more detail and information outlined here in this sample application that illustrates the problem and solution:
http://msdn.microsoft.com/en-us/library/ff966481.aspx
Additional Edit:
There is a pipeline in ASP.NET where WIF is configured. It hooks the authentication event and will pull the token from the cookie and build your IPrincipal so that subsequent code will have that in the context. You typically don't build the Principal yourself when using an STS. Instead, if you need to modify the Principal, you plugin to the pipeline in WIF and insert additional claims to the 'role' claim (actually a URI namespace). WIF will then use those claims to build the ClaimsPrincipal that will contain things like Roles and things just work (IsInRole, web.config auth, etc.).
If possible, it is best to have the token contain the roles as claims. This is a much longer discussion however around 'normalization' of claims to meaningful contexts. Remember, the claims you get from a IP-STS is in their own terms and they might not mean anything to your application. For example, I might get a claim from a customer that they are part of Adatum\Managers group. That is completely meaningless to my application, so what I would typically do is exchange that token for one that my app understands and in the process transform or normalize the claims by claim mappings (i.e. Adatum\Managers --> MyApplicationAdminRole). Windows Azure ACS service is very applicable here to help do that (normalize claims from different IPs).
I would recommend reading Vittorio's book on this all to get the common patterns here:
Eugenio's notes: Adding to what @dunnry wrote, which is all correct. The proper extensibility point to augment your claim set in the Relying Party (your web app) is by using a ClaimsAuthenticationManager. The docs for this type are here. there are pointers to samples in that page. In that class you would read the roles from the XML file and add them to the ClaimsIdentity. The rest of the app would not worry about claims, etc. (especially if you are using roles like in your case). The RSA config for the cookies encryption solves the load balancer issue.
Upvotes: 2
Reputation: 7636
Look at my post, I just did the same thing.
http://therubblecoder.wordpress.com/2011/10/25/wif-and-load-balancing-with-mvc-3/
Basically the claims token needs to be available to any cluster node, so using a certificate on the sessiontokenhandler will prevent a specific node processing the token in a manner specific to an instance.
In the microsoft.identity element in the config, you need to have an element that looks like this.
<serviceCertificate>
<certificateReference x509FindType="FindByThumbprint" findValue="****THUMBPRINT*****" storeLocation="LocalMachine" storeName="My" />
</serviceCertificate>
The application pool will also need to get access to this otherwise it won't be able to find the certificate by thumbprint.
The above code will use this certicate when dealing with the token. If you don't have this setup you will get a null reference exception.
Upvotes: 1