ADFS and SimpleSAMLphp with Drupal

These are instructions on how to configure SimpleSAMLphp library and Drupal on Pantheon, the configuration settings may vary depending on the ADFS configuration.

Requirements

  1. Download SimpleSAMLphp version 1.11.0. The newer versions available but I haven't had a chance to try them.
  2. Drupal 7 site (latest version always recommended).
  3. simpleSAMLphp Authentication Drupal 7 module.
  4. Drupal 7 site running on Pantheon.

Install SimpleSAMLphp

  • Create a private directory in your document root: /private and move downloaded files and folders to /private/simplesamlphp-1.11.0

  • Create a symlink to your simplesamlphp-1.11.0 directory from /simplesaml to /private/simplesamlphp-1.11.0/www

$ ln -s ./private/simplesamlphp-1.11.0/www ./simplesaml
$ git add ./private/simplesamlphp-1.11.0
$ git add simplesaml
$ git commit -m "Add SimpleSAMLphp library. Add SimpleSAML symlink."
  • Generate certificates as needed and add them to your repository /private/simplesamlphp-1.11.0/cert

Important: Make sure you are using correct encryption. Microsoft announced that they wouldn't be accepting SHA1 certificates after 2016. Read more. Generate SHA256 certificates:

$ cd cert
$ openssl req -x509 -sha256 -nodes -days 3652 -newkey rsa:2048 -keyout saml.pem -out saml.crt

SimpleSAMLphp Configuration

  • Set up your /private/simplesamlphp-1.11.0/config/config.php file
// Put this at the top of the config.php file
if (!ini_get('session.save_handler')) {
  ini_set('session.save_handler', 'file');
}

$ps = json_decode($_SERVER['PRESSFLOW_SETTINGS'], TRUE);
$host = $_SERVER['HTTP_HOST'];
$drop_id = $ps['conf']['pantheon_binding'];
$db = $ps['databases']['default']['default'];

Set the following parameters in the config = array():

// Make sure you have SSL certificate enabled
// if you would like to use HTTPS protocol.
'baseurlpath'             => 'https://'. $host .'/simplesaml/',
'certdir'                 => 'cert/',
'loggingdir'              => 'log/',
'datadir'                 => 'data/',
'tempdir'                 => '/srv/bindings/'. $drop_id . '/tmp/simplesaml',

// Change admin password.
'auth.adminpassword'      => '[YOUR_PASSWORD]',
// Set this to TRUE
'admin.protectindexpage'  => true,
// Probably better to set this to TRUE
'admin.protectmetadata'   => false,
    
// Change this salt
'secretsalt'              => '[SALT_CHANGE_THIS]',
    
// Update contact information
'technicalcontact_name'   => 'Contact Name',
'technicalcontact_email'  => 'email@example.com',
    
'enable.saml20-idp'       => true,
'session.cookie.secure'   => true,
    
'store.type' => 'sql',
'store.sql.dsn'           => 'mysql:host='
  . $db['host'] 
  . ';port='. $db['port'] 
  . ';dbname=' . $db['database'],
'store.sql.username'      => $db['username'],
'store.sql.password'      => $db['password'],
  • Now you should be able to login to your SimpleSAMLphp interface.

    • Visit http://dev-example.pantheon.io/simplesaml
    • Enter the password from the config.php value of the auth.adminpassword parameter.
    • Make sure Checking your PHP installation on the config page has no red flags. http://dev-example.pantheon.io/simplesaml/module.php/core/frontpage_config.php
    • Make sure SAML 2.0 IdP is green.
  • Next, configure /private/simplesamlphp-1.11.0/config/authsources.php file

$config = array(
  'admin' => array(
    'core:AdminPassword',
  ),
  'default-sp' => array(
   'saml:SP',

   // You can get this from ADFS Federation file
   // Contact your ADFS administrator
   // to obtain this information.
   'entityID'             => 'urn:drupal:adfs-saml',
   'idp'                  => 'http://example.org/adfs/services/trust',
   'NameIDPolicy'         => null,
   'redirect.sign'        => true,
   'assertion.encryption' => true,
   'sign.logout'          => true,

   // Generate using openssl, @see example above.
   // These are the certs from `/cert` directory.
   'privatekey'           => 'saml.pem',
   'certificate'          => 'saml.crt',
   // Defaults to SHA1 (http://www.w3.org/2000/09/xmldsig#rsa-sha1)
   'signature.algorithm'  => 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha256',
  ),
);
  • Next you will need a Federation metadata file from the ADFS. Contact your ADFS administrator to generate the file.

  • After you obtain the federation metadata file use the XML to SimpleSAMLphp metadata converter to generate other config files. The converter is a part of the SimpleSAMLphp library and can be accessed through web: http://dev-example.pantheon.io/simplesaml/admin/metadata-converter.php

The result should look similar to the following:

saml20-sp-remote

Put the results of this section into /private/simplesamlphp-1.11.0/metadata/saml20-sp-remote.php

$metadata['http://example.org.org/adfs/services/trust'] = array (
  'entityid' => 'http://example.org/adfs/services/trust',
  'contacts' => 
  array (
    0 => 
    array (
      'contactType' => 'support',
    ),
  ),
  'metadata-set' => 'saml20-sp-remote',
  'AssertionConsumerService' => 
  array (
    0 => 
    array (
      'Binding' => 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST',
      'Location' => 'https://example.org/adfs/ls/',
      'index' => 0,
      'isDefault' => true,
    ),
    1 => 
    array (
      'Binding' => 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact',
      'Location' => 'https://example.org/adfs/ls/',
      'index' => 1,
    ),
    2 => 
    array (
      'Binding' => 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect',
      'Location' => 'https://example.org/adfs/ls/',
      'index' => 2,
    ),
  ),
  'SingleLogoutService' => 
  array (
    0 => 
    array (
      'Binding' => 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect',
      'Location' => 'https://example.org/adfs/ls/',
    ),
    1 => 
    array (
      'Binding' => 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST',
      'Location' => 'https://example.org/adfs/ls/',
    ),
  ),
  'NameIDFormat' => 'urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress',
  'keys' => 
  array (
    0 => 
    array (
      'encryption' => true,
      'signing' => false,
      'type' => 'X509Certificate',
      'X509Certificate' => .... certificate string ...',
    ),
    1 => 
    array (
      'encryption' => false,
      'signing' => true,
      'type' => 'X509Certificate',
      'X509Certificate' => '.... certificate string ...',
    ),
  ),
  'saml20.sign.assertion' => true,
);

saml20-idp-remote

Put the results of this section into /private/simplesamlphp-1.11.0/metadata/saml20-idp-remote.php

$metadata['http://example.org/adfs/services/trust'] = array (
  'entityid' => 'http://example.org/adfs/services/trust',
  'contacts' => 
  array (
    0 => 
    array (
      'contactType' => 'support',
    ),
  ),
  'metadata-set' => 'saml20-idp-remote',
  'SingleSignOnService' => 
  array (
    0 => 
    array (
      'Binding' => 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect',
      'Location' => 'https://example.org/adfs/ls/',
    ),
    1 => 
    array (
      'Binding' => 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST',
      'Location' => 'https://example.org/adfs/ls/',
    ),
  ),
  'SingleLogoutService' => 
  array (
    0 => 
    array (
      'Binding' => 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect',
      'Location' => 'https://example.org/adfs/ls/',
    ),
    1 => 
    array (
      'Binding' => 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST',
      'Location' => 'https://example.org/adfs/ls/',
    ),
  ),
  'ArtifactResolutionService' => 
  array (
  ),
  'keys' => 
  array (
    0 => 
    array (
      'encryption' => true,
      'signing' => false,
      'type' => 'X509Certificate',
      'X509Certificate' => '.... certificate string ...',
    ),
    1 => 
    array (
      'encryption' => false,
      'signing' => true,
      'type' => 'X509Certificate',
      'X509Certificate' => '.... certificate string ...',
    ),
  ),
);
  • Finally you will have to provide information to your ADFS administrator. The SimpleSAMLphp library can generate the federation metadata file for you. Here this the link:

http://dev-example.pantheon.io/simplesaml/module.php/saml/sp/metadata.php/default-sp?output=xhtml

The federation metadata XML file should look something like this:

<?xml version="1.0"?>
<md:EntityDescriptor xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata" xmlns:ds="http://www.w3.org/2000/09/xmldsig#" entityID="urn:drupal:adfs-saml">
  <md:SPSSODescriptor protocolSupportEnumeration="urn:oasis:names:tc:SAML:1.1:protocol urn:oasis:names:tc:SAML:2.0:protocol">
    <md:KeyDescriptor use="signing">
      <ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
        <ds:X509Data>
          <ds:X509Certificate>.... certificate string .....</ds:X509Certificate>
        </ds:X509Data>
      </ds:KeyInfo>
    </md:KeyDescriptor>
    <md:KeyDescriptor use="encryption">
      <ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
        <ds:X509Data>
          <ds:X509Certificate> .... certificate string .....</ds:X509Certificate>
        </ds:X509Data>
      </ds:KeyInfo>
    </md:KeyDescriptor>
    <md:SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="https://dev-example.pantheon.io/simplesaml/module.php/saml/sp/saml2-logout.php/default-sp"/>
    <md:SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:SOAP" Location="https://dev-example.pantheon.io/simplesaml/module.php/saml/sp/saml2-logout.php/default-sp"/>
    <md:AssertionConsumerService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="https://dev-example.pantheon.io/simplesaml/module.php/saml/sp/saml2-acs.php/default-sp" index="0"/>
    <md:AssertionConsumerService Binding="urn:oasis:names:tc:SAML:1.0:profiles:browser-post" Location="https://dev-example.pantheon.io/simplesaml/module.php/saml/sp/saml1-acs.php/default-sp" index="1"/>
    <md:AssertionConsumerService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact" Location="https://dev-example.pantheon.io/simplesaml/module.php/saml/sp/saml2-acs.php/default-sp" index="2"/>
    <md:AssertionConsumerService Binding="urn:oasis:names:tc:SAML:1.0:profiles:artifact-01" Location="https://dev-example.pantheon.io/simplesaml/module.php/saml/sp/saml1-acs.php/default-sp/artifact" index="3"/>
  </md:SPSSODescriptor>
  <md:ContactPerson contactType="technical">
    <md:GivenName>Minnur</md:GivenName>
    <md:SurName>Yunusov</md:SurName>
    <md:EmailAddress>email@example.com</md:EmailAddress>
  </md:ContactPerson>
</md:EntityDescriptor>

Drupal Configuration

Basic setup

  • Open https://dev-example.pantheon.io/admin/config/people/simplesamlphp_auth
  • Check Activate authentication via SimpleSAMLphp option.
  • Check Force https for login links option. Make sure your server has SSL certificate.
  • Configure settings.php. Add the following:
    $ps = json_decode($_SERVER['PRESSFLOW_SETTINGS'], TRUE);
    $conf['simplesamlphp_auth_installdir'] = '/srv/bindings/'. $ps['conf']['pantheon_binding'] .'/code/private/simplesamlphp-1.11.0';

User info and syncing

  • Usually we set username as user's email address, if this is the case for you please try add the following:
    • http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress
  • Unique identifier for the user
    • http://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn
  • User mail address
    • http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress
  • Hit Save configuration

ADFS Configuration

See Useful Links below. Some of those links contain information on how to configure the ADFS.

Test SimpleSAMLphp and Drupal

  • Try login, use the following URL address to test:
    • https://dev-example.pantheon.io/saml_login
  • If you would like to support login via SAML only I would recommend you to replace Login (user/login) path with /saml_login

Troubleshoot

  1. Login works but logout doesn't.
    • Make sure your certificates are generated using correct encryption.
    • Make sure your ADFS configuration is set to use correct encryption. In my case it was set to SHA256 and the certificates were generated using SHA1.
  2. If you're using SHA256 certificates make sure to specify encryption algorithm in config/authsources.php by adding signature.algorithm. See SAML 2.0 Options section.
  3. Get errors on login.
    • Make sure ADFS has correct claim rules. All fields should be in it's own Claim Rule.
  4. Make sure to restart ADFS service every time you make a change.
  5. If login used to work but stopped working after a year/month etc. Most likely the certificate expiration date was set to be a year/month. To solve this issue simply regenerate the certificate. See example above.

Useful links