After having blogged a couple of times about how to build a simple STS, how to use claims based authentication in MVC 4.5 and how to set up federation with Azure Access Control Service, I thought it might be time to post a quick walkthrough of how to set up a simple federation with an existing STS.
Why did I think of that right now? Well, the pretty awesome “Identity and Access Tool” extension to Visual Studio has been removed from later versions of Visual Studio, making setting up federation a manual task. Unless you do it as you set up your application... And having been playing around with federation for a couple of days now in a project that wasn’t set up from scratch, I decided to just add a quick blog post on how to do a simple set up with the least amount of effort.
Disclaimer: This post assumes that you already have an Security Token Service set up and just need to integrate with it! And…it I s a very basic walkthrough…
The first thing we need to do is to set up the federation configuration in web.config. This is done through 2 configuration elements called <system.identityModel /> and <system.identityModel.services />. However, these are not available by default, so we have to add those sections. This is done by adding the following to web.config.
<configuration>
<configSections>
<section name="system.identityModel" type="System.IdentityModel.Configuration.SystemIdentityModelSection, System.IdentityModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089" />
<section name="system.identityModel.services" type="System.IdentityModel.Services.Configuration.SystemIdentityModelServicesSection, System.IdentityModel.Services, Version=4.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089" />
</configSections>
...
</configuration>
As soon as these new sections have been defined, it is time to do the actual configuration.
Remember, adding security federation like this requires you to add a reference to System.IdentityModel and System.IdentityModel.Services…
Let’s start by configuring the <system.identityModel.services /> section, which contains the federation information. Things like where to find the STS, what certificate the STS uses to encrypting the token and so on.
Inside the <system.identityModel.services /> there is a single element called <federationConfiguration />. In this element there are a few things that needs to be added and configured. Let’s start with the <wsFederation /> element. This element contains information about where the STS is located, whether or not to automatically issue redirects, as well as a whole lot of things regarding how to format the URLs used when redirecting the user to login and logout etc.
In my case, it looks like this
<system.identityModel.services>
<federationConfiguration>
<wsFederation issuer="http://sts/"
realm="http://WSFedTest/"
reply="http://localhost:53520/"
passiveRedirectEnabled="true"
requireHttps="false" />
...
</federationConfiguration>
</system.identityModel.services>
Ok, so what does this say? Well, first of all we have an issuer. The issuer is the address of the STS that we rely on to get tokens. Next is the “realm”, which is a string that identifies the application that is requesting the token. In my case I use a URL of my application, but to be honest, I think it could be pretty much anything. It is supposed to be a Uri though…I think…
The “reply” attribute defines where the STS should be posting the token to. In most cases, this will be the current application’s address. In my case that would be my local machine on a specific port as I am using using “Cassini”, the built in webserver from Visual Studio. Some STS:s has this Url configured in the STS configuration instead of having it passed as a parameter from the client.
“requireHttps” defines whether or not the Url:s being used have to use transport level security (SSL). For local development it is ok to turn this off, but don’t do it in production… If you forget to turn it off, and use non HTTPS Urls, the application will tell you…using a yellow screen of death…
And finally, the “passiveRedirectEnabled” defines whether or not users should be automatically redirected to the STS when considered unauthorized.
There are 2 important things to note about the “passiveRedirectEnabled” flag. Turning it on makes no difference what so ever unless you add a module called WSFederationAuthenticationModule. This will be added in just a couple of minutes.
Neither will it redirect users straight away when they enter the site. They will only be redirected when a HTTP 401 is being returned to the client.
If “passiveRedirectEnabled” is enabled, the WSFederationAuthenticationModule will look at all outgoing responses, trying to find HTTP 401s. If it finds a 401, it will modify the response and turn it into a redirect to the STS…
Ok, that was the <wsFederation /> element. Next up is the <cookieHandler /> element. This is used to configure how the cookie that in the end is issued to the client is handled. In most cases it can be left out. But for development, it might be useful to turn off the need for SSL. If this isn’t turned off, and you try to use non-ssl based addresses, then you will once again be told by the application in the way of a YSOD.
Turning it off is simple though… You do it like this
<system.identityModel.services>
<federationConfiguration>
...
<cookieHandler requireSsl="false" />
...
</federationConfiguration>
</system.identityModel.services>
And if you don’t like the default cookie name (FedAuth) you can change it by setting the name attribute of the <cookieHandler /> element, together with a whole heap of other cookie related settings.
The last element that I put in here is the <serviceCertificate /> element. This element is not required if your STS do not encrypt the token. If it does, you need to tell the application what certificate is being used to encrypt the token.
In my case, the token is being encrypted, so I need it. However, it isn’t that complicated to set up. All that is required is to define where the application can find the certificate.
In my case, the cert is located in the local machine’s “My” store (“Personal” in the MMC certificates snap-in). So my config looks like this
<system.identityModel.services>
<federationConfiguration>
...
<serviceCertificate>
<certificateReference x509FindType="FindByThumbprint"
findValue="XXXXX"
storeLocation="LocalMachine"
storeName="My" />
</serviceCertificate>
...
</federationConfiguration>
</system.identityModel.services>
As you can see, I am telling the system to locate my cert using the thumbprint… And yes, I have removed the thumbprint in the snippet above. Not that it really matters, but it was long and ugly…
There are a couple of things to remember here. First of all, the user that the application is being run under must have access to the certificate store. By default, it won’t. So it has to be given access.
If you forget to configure the service certificate, or put in the wrong certificate information, you will be greeted with a YSOD saying
ID4036: The key needed to decrypt the encrypted security token could not be resolved from the following security key identifier
If you on the other hand put in the wrong details, and the cert isn’t found, it will tell you that with a nice YSOD that said exactly what is wrong.
Next, do NOT get the thumbprint from the MMC snap-in. At least not by copying it. It contains invisible characters that will make the lookup fail. More information, and a way to do it instead, can be found here. Or, you could manually write down the thumbprint, and thus not get the hidden characters included…
Ok, that was the <system.identityModel.services /> element. Next up is the <system.identityModel />, which is used to configure the applications usage of the token.
Inside the <system.identityModel /> element, you put an element called <identityConfiguration />. Inside this, the first element to include is the <audienceUris /> element. This defines what audiences Uris that the issued tokens must have to be considered valid.
Exsqueeze me? Audience Uris? Well, the token being issued will contain an “audience Uri” that says “where” the token is valid, or what audiences are intended to use it. In my case, where the user will just be redirected to the STS, and then come straight back, the audience Uri corresponds to the “realm” that was defined before. In more complicated scenarios, the audience Uri could be different if the token came from somewhere else, or if the STS was configured differently… And by the way, the configuration could be a list of Uris. But in this case, it looks like this
<system.identityModel>
<identityConfiguration>
<audienceUris>
<add value="http://WSFedTest/" />
</audienceUris>
...
</identityConfiguration>
</system.identityModel>
Ok, so now the application knows to trust tokens with the defined Uri.
Next, it is time to tell the application how to, based on the received token, figure out what issuer (STS) was used to issue it. This is done by adding a <issuerNameRegistry /> element. In most cases, the issuer name registry will be an instance of the class ConfigurationBasedIssuerNameRegistry, which is defined using the “type” attribute.
When using a ConfigurationBasedIssuerNameRegistry, you configure the known issuers using a sub-element called <trustedIssuers />. Inside this element, it is possible to configure trusted token issuers by defining the name of the issuer and the thumbprint of the cert used to sign the token.
In my case, the STS uses the same cert to encrypt and sign the token, so the thumbprint will be the same as the one used for the decryption config. Like this:
<system.identityModel>
<identityConfiguration>
...
<issuerNameRegistry type="System.IdentityModel.Tokens.ConfigurationBasedIssuerNameRegistry, System.IdentityModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<trustedIssuers>
<add thumbprint="XXX" name="MySTS" />
</trustedIssuers>
</issuerNameRegistry>
...
</identityConfiguration>
</system.identityModel>
Ok, there is just one last thing for to configure, and that is certification validation. And the only reason for this is that I am using a self-signed cert.
As I am using a self-signed cert, I want to turn off certification validation, which is done using the <certificateValidation /> element. It is fairly simple and looks like this
<system.identityModel>
...
<identityConfiguration>
<certificateValidation certificateValidationMode="None" />
</identityConfiguration>
</system.identityModel>
Ok, it isn’t “fairly simple”…it is ridiculously simple… But remember, in production, when using proper certs and things, including validation is a good idea…
So…now that the configuration is done, it is pretty tempting to try it out. However, before we can do so, the application needs to return a 401 in some way, And the easiest is probably to either configure the web.config with a <location /> element, and turn off public access, or by creating a new MVC controller and either return an HttpUnauthorizedResult or add an AuthorizeAttribute. However, doing either of these will just give you a YSOD telling you that you don’t have access to the requested page.
Why is that? Well, there is one more thing to do. Or rather 2… There are 2 HttpModules that need to be added to the application for the authentication to start working. It is the WSFederationAuthenticationModule (FAM), and the SessionAuthenticationModule (SAM), and they are added like this
<system.webServer>
<modules>
<add name="WSFederationAuthenticationModule" type="System.IdentityModel.Services.WSFederationAuthenticationModule, System.IdentityModel.Services, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" preCondition="managedHandler" />
<add name="SessionAuthenticationModule" type="System.IdentityModel.Services.SessionAuthenticationModule, System.IdentityModel.Services, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" preCondition="managedHandler" />
</modules>
</system.webServer>
The FAM is responsible for looking at the incoming requests and outgoing responses to figure out if the the incoming request is a federation token that needs to be handled, or if the outgoing response is a 401 that needs to be turned into a redirect to the STS.
Once a token has been received and parsed, the FAM uses the SAM to issue a cookie that contains the user identification. This token is then used by the SAM on subsequent requests to authenticate the user.
Both of these modules are available in code through the static class called FederatedAuthentication. Using this class, we can for example sign out by using the SAM like this
FederatedAuthentication.SessionAuthenticationModule.SignOut();
Now, you can run off and test it!
One thing to note regarding the sign out mentioned above is that it will only sign the user out locally. If the STS is configured to offer Single Sign-On, you will probably need to notify the STS about the sign-out so that it can perform single sign-out if required. But that is beyond this post...
That’s it for this time! Cheers!