Single Sign On (SSO) is one of those things that makes the web a little bit easier to use. You might have a site that is actually a collection of any number of separate sites and applications, and your users are going to get confused if they need a separate username and password for each of them. To mention nothing of how sick they're going to get of typing them in every time they move from one section of your site to another.
Wouldn't it be easier to have a central repository for user details, against which all your sites validate any login attempt by a user and confirm whether they can enter or not? And wouldn't it be even better if a user who was already logged in on one site was automatically waved in on any other sites using the same details? You'd need something that could take login attempts from any number of different platforms and translate them into something your central system could understand. That something, essentially, is SSO.
We recently had a client with an existing Drupal site with a user base who needed an SSO solution. They were setting up a hosted Canvas learning management system (http://www.canvaslms.com) and they want their existing Drupal users to be able to access it seamlessly. The Canvas site we had to integrate with supported Single Sign On, but only through CAS and SAML. As the client wished to use pre-existing Drupal users as their Canvas user base, we needed to turn the existing site into an Identity Provider (IdP): we turned to the drupalauth for SimpleSAMLphp module (https://code.google.com/p/drupalauth). This module allows a Drupal site to act as a SAML IdP, allowing users to have their Drupal user credentials work on any other site that uses a SAML Service Provider (SP) to validate user access.
The first step is to set up SimpleSAMLphp (https://simplesamlphp.org), which is done by downloading the module and deploying it to your server. You will then need to add an alias onto your Drupal site's host information (the “sites-available” bit of apache2 or nginx) to alias /simplesaml to the folder on your server where you have put the SimpleSAMLphp files.
To configure the module, edit the simplesamplephp/config/local.config.php file to add an admin password and set the store to use MySQL:
$config['store.type'] = 'sql';
/*
* The DSN the sql datastore should connect to.
*
* See http://www.php.net/manual/en/pdo.drivers.phpfor the various
* syntaxes.
*/
$config['store.sql.dsn'] = 'mysql:host=localhost;dbname=<your DB>';
/*
* The username and password to use when connecting to the database.
*/
$config['store.sql.username'] = '<MySQL username>';
$config['store.sql.password'] = '<MySQL password';
/*
* The prefix we should use on our tables.
*/
$config['store.sql.prefix'] = '<your prefix>';
You will – of course – also need to make sure the database and user account exist on your server's MySQL installation.
After this, you need to install drupalauth: this is reasonably well documented in the drupalauth READ ME (https://code.google.com/p/drupalauth/wiki/INSTALLATION), and mostly again involves moving files around. There is a module to go in the simplesamlphp/modules folder and a Drupal module, which needs to be enabled and configured. Following the installation instructions, we set up an External authsource for authentication, which means that if a user has not logged in they get directed to the Drupal site. The alternative is a User Pass authsource, in which SimpleSAML will present its own login page to the user.
The Drupal side of the authentication is handled by storing the logged in user's user id in a secure cookie. If you follow the instructions and everything is working, you should be able to visit /simplesaml/module.php/core/authenticate.php?as=drupal-userpass on your site and get an output of your user details. You will also see the cookie in your browser's cookie folder.
At this point, you might think you have set everything up and your work is done. But SAML doesn't authenticate using the cookie: the cookie is not needed by anything except the IdP, which uses it to pass the user account from Drupal to where it can be used inside SimpleSAMLphp. SAML instead authenticates in two stages: there is the IdP (our Drupal site) which holds user data and will confirm a log in attempt is genuine, and then there is the SP which sends the log in request to the IdP and handles the return. In order to actually use the Drupal site as a single sign on provider, we needed to set up a separate SP that the Canvas (or any other) site can send its log in attempts through.
This is also relatively simple and well documented (although not from the point of view of using the drupalauth module). The Canvas site we are integrating with can only receive SAML data in the SAML2 format, so this is what I will tell you how to set up.
The first thing you will need to do is amend the SimpleSAMLphp config to turn on SAML2. In simplesamlphp/config/config.php there is a setting "'enable.saml20-idp' => false,” which needs to be amended to read “true” (you can also turn on other authentication types here). While you're there, take a note of the “'certdir' => 'cert/',” value: SAML2 requires secure connections between the IdP and the SP, which are managed by authentication certificates. You can create your own, but SAML will look for them by default in the certdir.
To create your certificates, move to the folder simplesamlphp/cert (or whatever value was in the certdir variable: you may need to create the folder) and then run the following command:
openssl req -newkey rsa:2048 -new -x509 -days 3652 -nodes -out server.crt -keyout server.pem
This will generate secure certificates and keys that can be referred to by SAML.
You will now need to set up the metadata for the SP.
SAML will only allow SAML requests from providers that it has been told about: this you do by adding metadata. In the folder simplesamlphp/metadata is a file saml20-sp-remote.php where the data for the SAML2 SP we are setting up needs to be stored.
The Canvas site we were setting up the Drupal site to integrate with provided its own XML format metadata for us to add to the configuration (XML can be turned into the required PHP using SimpleSAML: visit /simplesaml/admin/metadata-converter.php as the SimpleSAMLphp admin on your site and paste in the XML file). This meant that for us most of the configuration was copy and paste, but if you are setting up from scratch there is an example in the metadata file. The important thing is that the key for the entry in the metadata array matches the Entity ID that is in the SAML request sent by your SP, and that you provide the AssertionConsumerService url: this is the URL that the SAML return will be fired at, and if it is missing your login request will stop once it reaches the IdP.
You may also need to tell SAML which variable you are using to identify your users. The Canvas site we were integrating with matched users by their Drupal user name, which it needed to be returned in the SAML return as the NAME ID variable. If you don't tell SAML this, it generates a random string to be the NAME ID, which may be fine on your system but caused ours to return a “user not found” error on the Canvas site. You can specify a value from the Drupal user account to be used by setting:
'simplesaml.nameidattribute' => '<user value>',
'NameIDFormat' => 'urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress',
The <user value> must relate to a key on the Drupal user account object, or the alias you've set it to in the local.authsources.php file (the “callit” value). The NameIDFormat needs to be set, otherwise SAML will default to creating its own random NAME ID: don't worry that this one says emailAddress – this doesn't mean that it will attempt to use the email address. There are other options available, but this is the one that worked best for us: you may need to use another to get the correct response when you attempt to log in.
The last piece of the puzzle is telling SAML to use your Drupal site to check the user account against by default. This is done by again editing the local.authsources file. Add the following into the file:
$config['default-sp'] = array(
'saml:SP',
'privatekey' => 'server.pem',
'certificate' => 'server.crt',
// The entity ID of this SP.
// Can be NULL/unset, in which case an entity ID is generated based on the metadata URL.
'entityID' => '<Your Entity ID>',
'auth' => 'drupal-userpass',
// The URL to the discovery service.
// Can be NULL/unset, in which case a built in discovery service will be used.
'discoURL' => null,
);
The private key and the certificate need to match the names of your files in the /cert directory, and the entityID value needs to match the key you set in the metadata array in saml20-sp-remote.php. This then tells SAML that if it receives a request from an SP, it should use the metadata we set up in the remote file and carry out the authentication using the Drupal IdP we set up.
If everything is working, you should be able to send a valid SAML authentication request to the SAML2 parser on your site at /simplesaml/saml2/idp/SSOService.php (or a valid SAML logout request to /simplesaml/saml2/idp/SingleLogoutService.php) and you should be able to log in with your Drupal site's user account details.
There is a Drupal module to set up a Drupal site as a SimpleSAMLphp SP (simplesamlphp_auth), meaning that you can then set up a separate Drupal site to use the Drupal IdP you have just set up. As we didn't need to follow this step, however, I can't help with setting that up.
There is also a FireFox extension (https://github.com/UNINETT/SAML-tracer) which displays and SAML requests that are travelling through your browser, which can be very useful for debugging. You can also turn on logging in SimpleSAMLphp itself by editing the /simplesamlphp/config/config.php file: look for “'debug' => false,” in your file and set it to “true”, and then set “'logging.level' => SimpleSAML_Logger::ERR,” and “'logging.handler' => 'syslog',” to whatever you require: a logging level of ::DEBUG will give you all messages, and a handler of “file” will save a file in the /simplesamlphp/log folder if you don't have access to syslog.
Most of this was discovered through trial and error: if you think I've done something wrong, or know a better way to set up SimpleSAMLphp and Drupal please let me know in the comments.