As part of the rollout of Office 365 at the University of Hertfordshire, ADFS is being rolled out as a single sign on service.
Therefore I wanted to see how easy it would be to get a Rails application to use ADFS as an authentication provider. We were using omniauth-ldap, so I set out to find a ADFS equivalent. I found omniauth-wsfed. However initially had difficultly getting it working with ADFS 2.0, after switching to a beta version of the gem with SAML 1.1 support I got it working.
In this post I have detailed the process that I went through, and the components you need to setup ADFS within a Rails application.
Creating a relying party trust
Before you can setup ADFS within your Rails application you need your application to have a relying party trust setup on the ADFS Server. This tells the ADFS server to accept login requests for your application, and which user identifiers it should provide back to the application. The user identifiers are called claims.
There are two main ways to setup a new relying party trust, you can enter all the details of your application manually within the ADFS configuration tool, or you may be asked to provide a Federation Metadata XML document. Older versions of Visual Studio had a tool to create this document however Microsoft have removed this in more recent versions. Here is an example base level Federation Metadata XML document:
In this example you need to change the entityId, EndpointReference and PassiveRequestorEndpoint to match your applications domain. You may be asked to provide additional information about which claims you require but your Windows administrator should be able to advise if this is the case.
Setting up the Rails Application
To setup ADFS in Rails you need the following gems.
The main omniauth gem and the omniauth-wsfed gem. Note you need to use the 0.3.2.pre.beta version at the time of writing this, when using ADFS as only this version supports the SAML 1.1 response you will get back from ADFS.
You should create a file called omniauth.rb within config/initializers. This file will setup the details of your ADFS server. Within this file you need to include the following:
Issuer Name: This should be in the format of the adfs sever domain followed by /adfs/services/trust
Issuer: This is where your login requests will be sent, normally it will be the path /adfs/ls on the ADFS server.
Realm: This should match the domain that you provide in your federation metadata document
Reply: This is where you want the response from ADFS to be returned to in your application. This is normally the path /auth/wsfed/callback when using Omniauth.
SAML Version: Set this to 1. ADFS only seems to sent back SAML 1.1 responses, I haven't seen a way to get it to return SAML 2.0
ID Claim: This is the name of the claim field that ADFS will return that should be used as the unique identifier.
IDP Cert Fingerprint: Your Windows Administrator should be able to tell you this, but if not a way to find it is to put in any string, do a test login to ADFS - this will fail when doing the callback as the certificate doesn't match, however if you inspect the response in the Chrome Web Inspector you will be able to see the X509 Certificate in the response. You can then use OpenSSL tools, or this online tool to get the fingerprint of the certificate.
Setting up callback routes
The omniauth documentation recommends setting up the following routes:
The controller#action can differ depending on how your application is structured.
Logging in with ADFS
To create a login request, you just need to visit http://myapp.com/auth/wsfed within your application. This will automatically redirect you to the ADFS server which will either ask you to login or authenticate you if you are already signed in.
You will then be passed back to the callback URL that you setup in your config, the response will include any user claims that have been setup on the ADFS server.
Handling the callback
You can handle the callback in the same way you would any Omniauth provider.
In this case I am only accessing the uid value but if you had other claims you want you can access them in the same way.
You would then create a session etc as you would with any other authentication provider. You should also create a failure action, to handle any failed authentication callbacks.
Logging out a user is simple. You will want to destroy the users session and then pass them to the ADFS server to logout, you can pass to the ADFS server the location you want to user to be redirected back to
Depending on how long you persist your user sessions, a user could logout of ADFS via another application but remain logged in to your application. You may wish to make your cookie expiry times shorter and pass users through ADFS more often, remember if they are still signed in they won't be asked to enter a username and password each time. However it does cause a slight delay in the request while the user is passed to ADFS and back again so you wouldn't want to do this for every user request.
We have made Ask Herts live as the first application using ADFS and will begin to rollout further to our other Rails applications at the University.
These steps and configuration have worked well for us, but depending on the configuration of your ADFS server your millage may vary. If you get stuck, or have any feedback please leave a comment.