Shibboleth Authentication for Hyrax
13 Dec 2017 David Chandek-Stark
In a previous article I discussed a strategy for integrating Shibboleth authentication with a (then-called) Hydra application (that Hydra project has since been renamed to Samvera). The implementation details of this strategy in the Duke Digital Repository have evolved slightly in the interim, hopefully in the direction of improvement and simplification, and, now that we are engaged in a pilot project built on Hyrax 2.0, it seems useful to update this information.
Important! Since we intend to change the Hyrax user_key
from its default field email
, implementers are advised to start with clean user data if possible. We have observed issues in transitioning users from registrations based on email to Shibboleth identity information, at least where the email address differed from the person’s EPPN. This article does not cover how to work through those issues.
The Gemfile
Add gem 'omniauth-shibboleth'
to our Gemfile and bundle install
.
The Database Migration
We need to add a field to store our uid
value in the users
table.
The name of the field is not critical; choose your own and substitute accordingly in the rest of this article.
You may or may not want or need to index the field, depending on the number of users you expect to support.
If you set null: false
be sure to also provide a default value, say default: ""
.
We will use Rails validations in our example model to ensure presence and uniqueness.
The Devise Initializer
Now we update the Devise configuration set in its initializer.
The uid_field
mapping lambda indicates that we will use eppn
if available, or the field duDukeID
(a custom institutional identifier) otherwise.
See omniauth-shibboleth documentation
for details on this type of configuration.
If you just want to use eppn
as the uid, then write uid_field: "eppn"
, which is also the omniauth-shibboleth default.
The name_field
mapping to displayName
is the default for omniauth-shibboleth, but we’ll be explicit here.
The Model
In our User
model we first need to activate the Devise omniauth module and specify the omniauth provider:
Next we need to implement a class method that returns a User instance to sign in. We will use this method in our custom controller (below) to get/create the local user account to be signed in for the remote user authenticated via Shibboleth.
There’s nothing magic about the method name from_omniauth
; choose a different name if you like.
We will assume that “registration” of new users via Shibboleth authentication is automatic – i.e.,
that we will create a new User
if necessary for the provided credentials.
In order to integrate smoothly with Hyrax and its dependencies (e.g., Blacklight), we will change the #to_s
and #user_key
methods:
Finally, you may wish to implement validation of the uid
attribute, although it’s not required.
The Controller
Now we need to implement the shibboleth
action in OmniauthCallbacksController
. This is the bit that uses the authentication information from Shibboleth
to sign in with Devise.
We can generate the controller in the usual way with the Devise generator rails generate devise:controllers users
. At the moment we only care about OmniauthCallbacksController
; you may discard the other generated controllers as you see fit.
After adding the new action, here’s our updated controller:
The Routes
In order to route requests properly to our new controller, we need to update our routes config:
The Web Server
In our example we assume a Rails production environment under Passenger + Apache (2.4) with mod_shib. Hopefully readers can translate well enough to other configurations for this discussion to be useful.
The relevant bits of the virtual host configuration (not intended as a complete Rails/Passenger config example):
nginx Notes
nginx + shib + omniauth config may require setting the omniauth-shibboleth option request_type
to :header
in one or more configuration files:
Devise
Rack Middleware
(Credit: eefahy)
Development and Testing
For the Shibboleth integration piece itself we don’t need to customize any of the Devise views or controllers (other than OmniauthCallbacksController, above) because users on our site will not use registration or login forms provided by our application.
However, if we retain database_authenticatable
and registerable
functionality for development and testing,
then we need to update the default views to work with the changes we have made. Follow the Devise documentation for generating views to customize. You will have to replace references to email
with uid
, or add uid
as a required field to devise/sessions/new.html.erb
, etc. Note that if you generate views in the users
scope (which should only be necessary if you require multiple authentication scopes), you will also need to update the routing configuration.
Finally, because Hyrax still requires the email
field by default, you will need to modify the parameters permitted by Devise controllers by adding code such as this to your ApplicationController
:
It is also possible to write integration tests using OminAuth’s test mode and mocking features, although I have not yet tried to do so with omniauth-shibboleth.
The End
rake db:migrate
, reload the web server, and enjoy!
–DCS