Configuring SAML integration using SimpleSamlPhp

A customer-written documentation by Christiaan Brand, CTO at Entersekt.

Managing user identities in a corporate setting is often pretty challenging — even more so if the company is expanding rapidly and trying to integrate some of the best Cloud-hosted tools into their environment.

I’m Christiaan Brand, the CTO for Entersekt, a young, fast growing software security company based in Cape Town, South Africa. Among my other duties, I’m responsible for our general user control ecosystem.

I hear of so many companies where the logins to different systems are internally shared by employees, written down on sticky notes, stored inside applications responsible for managing passwords and other systems, which simply does not scale. It’s not rocket science, but it does take a bit of planning to get it right.

Read more about general user management concerns in my corresponding blogpost!

By integrating Small Improvements into our own SAML IdP (Identity Provider – the SAML system that does authentication on behalf of Service Providers) our users can use their existing AD credentials to sign into Small Improvements. Of course, you don’t have to be using AD as your backend identity store — pretty much any central list of user identities will do — but in the next section I will explain how to use simplesamlphp and the LDAP-connector to integrate into AD’s LDAP provider.

SimpleSAMLphp is an open-source and very lightweight, easy to use SAML IdP and SP. If you want to get started with being a SAML Identity Provider for your organization I’d recommend that you head over to http://simplesamlphp.org/

To get everything going I had to make a few changes to my SimpleSAMLphp after I’d installed it:

  1. Installation consists of downloading the package, unzipping the contents, and making it accessible via your webserver of choice. My installation runs on a Fedora Core 15 machine with Apache.
  2. I had to make two slight modifications to the source code to get it to work. Both changes are in the lib\SAML2\HttpRedirect.php file:
    1. Inside public function receive()
      For some or other reason, the function expects the SAMLRequest to be gzipped. It’s not — ever. So I just commented out the following line:
      // $msg = gzinflate($msg);
      		

      There’s a more elegant way to do it – detecting if the data is gzipped, and if so, performing an inflate. I was too lazy to do this, but might get someone in the office to do it for me later.

    2. Just before the line:
      $document->loadXML($msg);
      		
      I had to insert:
      $document->recover = TRUE;
      		

      There was some or other binary garbage at the end of $msg. Simply setting recover to TRUE was the easiest way to get the loadXML function to simply ignore it.

Once I’d made these changes to SimpleSAMLphp, I could continue with the actual configuration:

  1. In config/config.php there are a couple self-explanatory configuration items you have to complete, amongst others:
    1. 'enable.saml20-idp' => true,
    2. There’s another pretty important piece of config code – but we’ll get to that a bit later
  2. In config/authsources.php you have to enable whatever mechanism you want to use for authentication:
    1. For testing, I’d recommend uncommenting 'example-userpass' and remember to create the relevant “enable” file in that authentication modules directory (see the simplesamlphp documentation)
    2. For production, I set up 'ldap:LDAP' to authenticate against my Windows 2008 Domain Controller:
      'example-ldap' => array( 'ldap:LDAP',  // Give the user an option to save their username for future login attempts // And when enabled, what should the default be, to save the username or not //'remember.username.enabled' => FALSE, //'remember.username.checked' => FALSE,  // The hostname of the LDAP server. 'hostname' => 'ldap.entersect.co.za',  // Whether SSL/TLS should be used when contacting the LDAP server. You should set it to TRUE in production. You can test with it set to FALSE if you’d like. 'enable_tls' => FALSE,  // Whether debug output from the LDAP library should be enabled. // Default is FALSE. 'debug' => FALSE,  // The timeout for accessing the LDAP server, in seconds. // The default is 0, which means no timeout. 'timeout' => 30,  // Set whether to follow referrals. AD Controllers may require FALSE to function. 'referrals' => FALSE,  // Which attributes should be retrieved from the LDAP server. // This can be an array of attribute names, or NULL, in which case // all attributes are fetched. 'attributes' => NULL,  // The pattern which should be used to create the users DN given the username. // %username% in this pattern will be replaced with the users username. // // This option is not used if the search.enable option is set to TRUE. //	'dnpattern' => 'uid=%username%,ou=people,dc=example,dc=org',  // As an alternative to specifying a pattern for the users DN, it is possible to // search for the username in a set of attributes. This is enabled by this option. 'search.enable' => TRUE,  // The DN which will be used as a base for the search. // This can be a single string, in which case only that DN is searched, or an // array of strings, in which case they will be searched in the order given. 'search.base' => 'ou=Users, ,dc=entersect,dc=local',  // The attribute(s) the username should match against. // // This is an array with one or more attribute names. Any of the attributes in // the array may match the value the username. 'search.attributes' => array('samAccountName'),  // The username & password the simpleSAMLphp should bind to before searching. If // this is left as NULL, no bind will be performed before searching. 'search.username' => 'ldapusername@entersect.local', 'search.password' => 'myldappassword',  // If the directory uses privilege separation, // the authenticated user may not be able to retrieve // all required attribures, a privileged entity is required // to get them. This is enabled with this option. 'priv.read' => FALSE,  // The DN & password the simpleSAMLphp should bind to before // retrieving attributes. These options are required if // 'priv.read' is set to TRUE. 'priv.username' => NULL, 'priv.password' => NULL,  ),
      		

The next is the configuration specific to Small Improvements. This is all in the metadata subdirectory:
In saml20-idp-hosted.php:

/* X.509 key and certificate. Relative to the cert directory. */ 'privatekey' => 'si.pem', 'certificate' => 'si.crt',

This should match whatever certificate and privatekey you want to use. You can use the defaults for testing, but please generate new ones (at least 2048-bit RSA) before going into production (using openssl).

/* * Authentication source to use. Must be one that is configured in * 'config/authsources.php'. */ 'auth' => 'example-ldap',

You should also set the “auth” parameter to whatever authentication type you want to use.
In saml20-sp-remote.php:

$metadata['https://entersekt.small-improvements.com'] = array( 'AssertionConsumerService' => 'https://entersekt.small-improvements.com/saml/consume', 'NameIDFormat' => 'urn:oasis:names:tc:SAML:2.0:nameid-format:email', 'simplesaml.nameidattribute' => 'test', 'simplesaml.attributes' => FALSE, 'ForceAuthn' => FALSE, 'SingleLogoutService' => 'https://entersekt.small-improvements.com/logout', );

The configuration above is pretty self-explanatory: AssertionConsumerService is the URL to which the result of the login should be posted back. The subdomain component of this should reflect your own account.
'simplesaml.nameidattribute': You probably want this one to be a specific attribute in LDAP (or in your provider) which references the email address. Unfortunately, neither “uid” or “mail” would ever map as it should. That’s why I wrote the following piece of code in config.php to go in just after authproc.idp => array(

10 => array( 'class' => 'core:PHP', 'code' => ' if (empty($attributes["mail"])) { throw new Exception("Missing mail attribute."); }  $uid = $attributes["mail"][0]; $mail = $uid; $attributes["test"] = array($mail); ', ),

It transfers the real mail attribute into “test” which is then accessible when formulating the SAMLResponse. I have a feeling it might have something to do with the following being enabled in saml20-idp-hosted.php, but I haven’t had time to check into it yet:

'authproc' => array( // Convert LDAP names to oids. 100 => array('class' => 'core:AttributeMap', 'name2oid'),

I don’t want to disable it here, as some of my other SAML SP’s work with it turned on.

You can test your simplesamlphp setup (and the relevant authsource) at any time by simply going to the website using a browser. If you’re happy that everything looks like it’s working, go to Small Improvements, and Enable SAML, by clicking on the One Login link under Administration.

The metadata URL should match whatever URL you have in $metadata['https://entersekt.small-improvements.com'] – mine is https://entersekt.small-improvements.com

The AssertionURL should be the path to SSOLogin.php on your simpleSAMLinstallation — mine is: https://backend.entersect.co.za …. You should obviously use SSL on your web server.

Paste your certificate in the block. You can grab it by opening the crt file you’ve specified with a text editor.

And that’s it!! And now you can use this same setup to also federate logins to Google Apps and SalesForce.com. They’re all SAML capable!

Still need help? Contact Us Contact Us