These are instructions on how to configure SimpleSAMLphp library for Drupal 8 on Acquia, the configuration settings may vary depending on the ADFS configuration.
Requirements
- Download SimpleSAMLphp (I used version 1.14.2, latest version is always recommended).
- Drupal 8 site (latest version recommended).
- simpleSAMLphp Authentication Drupal 8 module.
- Drupal 8 site running on Acquia.
Install SimpleSAMLphp
-
Extract the SimpleSAML library to the root directory of your repository alongside
docroot
,acquia-utils
andlibrary
. -
In your docroot directory, create a symbolic link (name it
simplesaml
) that points to the to thesimplesamlphp-1.14.2/www
directory in theSimpleSAMLphp library.
$ cd docroot
$ ln -s ../simplesamlphp-1.14.2/www ./simplesaml
$ git add ../simplesamlphp-1.14.2
$ git add simplesaml
$ git commit -m "Add SimpleSAMLphp library. Add SimpleSAML symlink."
- Generate certificates as needed and add them to your repository
/simplesamlphp-1.14.2/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 /simplesamlphp-1.14.2/config/config.php
file. Acquia provides a code snippet that helps properly setup all the variables required by the library. The code snippet shoud go AFTER the config = array()
variable:
$config = (
// Default Library settings template.
// I would not recommend to change any of those settings
// Instead just override them below the `$config` varaible.
);
// All custom changes below. Modify as needed.
// Defines account specific settings.
// $ah_options['database_name'] should be the Acquia Cloud workflow database name which
// will store SAML session information.set
// You can use any database that you have defined in your workflow.
// Use the database "role" without the stage ("dev", "stage", or "test", etc.)
$ah_options = array(
'database_name' => '[DATABASE-NAME]',
'session_store' => array(
'prod' => 'memcache', // This can be either `memcache` or `database`
'test' => 'memcache', // This can be either `memcache` or `database`
'dev' => 'database', // This can be either `memcache` or `database`
),
);
// Base URL
$config['baseurlpath'] = 'https://'. $_SERVER['HTTP_HOST'] .'/simplesaml/';
// Remove memcache prefix
unset($config['memcache_store.prefix']);
// Set some security and other configs that are set above, however we
// overwrite them here to keep all changes in one area
$config['technicalcontact_name'] = "Technical Contact Name";
$config['technicalcontact_email'] = "email@example.com";
// Change these for your installation
$config['secretsalt'] = '[YOUR-SECERET-SALT]';
$config['auth.adminpassword'] = '[ADMIN-PASSWORD]';
$config['admin.protectindexpage'] = TRUE;
// Prevent Varnish from interfering with SimpleSAMLphp.
setcookie('NO_CACHE', '1');
if (empty($_ENV['AH_SITE_ENVIRONMENT'])) {
// add any local configuration here
} else {
$ah_options['env'] = $_ENV['AH_SITE_ENVIRONMENT'];
$config = acquia_logging_config($config);
$config = acquia_session_store_config($config, $ah_options);
}
function acquia_session_store_config($config, $ah_options) {
if ($ah_options['session_store'][$ah_options['env']] == 'memcache') {
$config = mc_session_store($config);
} elseif ($ah_options['session_store'][$ah_options['env']] == 'database') {
$config = sql_session_store($config, $ah_options['database_name']);
}
return $config;
}
function acquia_logging_config($config) {
// Set log location, as specified by Acquia
$config['logging.handler'] = 'file';
$config['loggingdir'] = dirname($_ENV['ACQUIA_HOSTING_DRUPAL_LOG']);
$config['logging.logfile'] = 'simplesamlphp-' . date("Ymd") . '.log';
return $config;
}
function mc_session_store($config) {
$config['store.type'] = 'memcache';
$config['memcache_store.servers'] = mc_info();
return $config;
}
function mc_info() {
$creds_json = file_get_contents('/var/www/site-php/' . $_ENV['AH_SITE_NAME'] . '/creds.json');
$creds = json_decode($creds_json, TRUE);
$mc_server = array();
$mc_pool = array();
foreach ($creds['memcached_servers'] as $fqdn) {
$mc_server['hostname'] = preg_replace('/:.*?$/', '', $fqdn);
array_push($mc_pool, $mc_server);
}
return array($mc_pool);
}
function sql_session_store($config, $database_name) {
$creds = db_info($database_name);
$config['store.type'] = 'sql';
$config['store.sql.dsn'] = sprintf('mysql:host=%s;port=%s;dbname=%s', $creds['host'], $creds['port'], $creds['name']);
$config['store.sql.username'] = $creds['user'];
$config['store.sql.password'] = $creds['pass'];
$config['store.sql.prefix'] = 'simplesaml';
return $config;
}
function db_info($db_name) {
$creds_json = file_get_contents('/var/www/site-php/' . $_ENV['AH_SITE_NAME'] . '/creds.json');
$databases = json_decode($creds_json, TRUE);
$db = $databases['databases'][$db_name];
$db['host'] = ($host = ah_db_current_host($db['db_cluster_id'])) ? $host : key($db['db_url_ha']);
return $db;
}
function ah_db_current_host($db_cluster_id) {
require_once("/usr/share/php/Net/DNS2_wrapper.php");
try {
$resolver = new Net_DNS2_Resolver(array('nameservers' => array('127.0.0.1', 'dns-master')));
$response = $resolver->query("cluster-{$db_cluster_id}.mysql", 'CNAME');
$cached_id = $response->answer[0]->cname;
}
catch (Net_DNS2_Exception $e) {
$cached_id = "";
}
return $cached_id;
}
- The latest version of the Acquia SAML config snippet can be downloaded from here: https://gist.github.com/acquialibrary/8059715
- Notice:
$config['baseurlpath'] = 'https://'. $_SERVER['HTTP_HOST'] .'/simplesaml/';
. This forces SAML library to server over HTTPS. -
Now you should be able to login to your SimpleSAMLphp interface.
- Visit
https://example.prod.acquia-sites.com/simplesaml
- Enter the password from the
config.php
value of theauth.adminpassword
parameter. - Make sure Checking your PHP installation on the config page has no red flags.
https://example.prod.acquia-sites.com/simplesaml/module.php/core/frontpage_config.php
- Visit
-
Next, configure
/simplesamlphp-1.14.2/config/authsources.php
file
$env = !empty($_ENV['AH_SITE_ENVIRONMENT']) ? '-' . $_ENV['AH_SITE_ENVIRONMENT'] : '';
$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' . $env,
'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:
https://example.prod.acquia-sites.com/simplesaml/admin/metadata-converter.php
The result should look similar to the following:
saml20-sp-remote
Put the results of this section into /simplesamlphp-1.14.2/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 /simplesamlphp-1.14.2/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:
https://example.prod.acquia-sites.com/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://example.prod.acquia-sites.com/simplesaml/module.php/saml/sp/saml2-logout.php/default-sp"/>
<md:SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:SOAP" Location="https://example.prod.acquia-sites.com/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://example.prod.acquia-sites.com/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://example.prod.acquia-sites.com/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://example.prod.acquia-sites.com/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://example.prod.acquia-sites.com/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
http://example.prod.acquia-sites.com/admin/config/people/simplesamlphp_auth
- Check Activate authentication via SimpleSAMLphp option.
- Configure
settings.php
. Add the following:// SimpleSAMLphp configuration # Provide universal absolute path to the installation. if (isset($_ENV['AH_SITE_NAME']) && is_dir('/var/www/html/' . $_ENV['AH_SITE_NAME'] . '/simplesamlphp-1.14.2')) { $settings['simplesamlphp_dir'] = '/var/www/html/' . $_ENV['AH_SITE_NAME'] . '/simplesamlphp-1.14.2'; } else { // Local SAML path. if (is_dir(DRUPAL_ROOT . '/../simplesamlphp-1.14.2')) { $settings['simplesamlphp_dir'] = DRUPAL_ROOT . '/../simplesamlphp-1.14.2'; } }
- Modify your .htaccess file by adding the following:
# Copy and adapt this rule to directly execute PHP files in contributed or # custom modules or to run another PHP application in the same directory. RewriteCond %{REQUEST_URI} !/core/modules/statistics/statistics.php$ + # Allow access to simplesaml paths + RewriteCond %{REQUEST_URI} !^/simplesaml # Deny access to any other PHP files that do not match the rules above. RewriteRule "^.+/.*\.php$" - [F]
- SimpleSAMLphp_auth module settings. I personally recommend to store configuration for SimpleSAMLphp_auth module settings in
settings.php
.$config['simplesamlphp_auth.settings'] = [ // Basic settings. 'activate' => TRUE, // Enable or Disable SAML login. 'auth_source' => 'default-sp', 'login_link_display_name' => 'Login with your SSO account', 'register_users' => TRUE, 'debug' => FALSE, // Local authentication. 'allow' => [ 'default_login' => TRUE, 'set_drupal_pwd' => TRUE, 'default_login_users' => '', 'default_login_roles' => [ 'authenticated' => FALSE, 'administrator' => 'administrator', ], ], 'logout_goto_url' => '', // User info and syncing. // `unique_id` is specified in Transient format, otherwise this should be `UPN` // Please talk to your SSO adminsitrators about which format you should be using. 'unique_id' => 'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn', 'user_name' => 'uid', 'mail_attr' => 'mail', 'sync' => [ 'mail' => FALSE, 'user_name' => FALSE, ], ];
Local authentication
- Check Allow authentication with local Drupal accounts option. Otherwise you other users won't be able to login to Drupal.
- Hit Save configuration
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://example.prod.acquia-sites.com/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
- 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.
- If you're using SHA256 certificates make sure to specify encryption algorithm in
config/authsources.php
by addingsignature.algorithm
. See SAML 2.0 Options section. - Get errors on login.
- Make sure ADFS has correct claim rules. All fields should be in it's own Claim Rule.
- Make sure to restart ADFS service every time you make a change.
- 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.
- If you get Permission Denied message when accessing
https://example.prod.acquia-sites.com/simplesaml
make sure you properly modified your.htaccess
file. More help with the simpleSAMLphp Authentication for Drupal 8 can be found in the module's README file. - In case if you can't access
https://example.prod.acquia-sites.com/simplesaml
page and you see some technical issue message, try changing session_store frommemcache
todatabase
. - Can't login and SimpleSAMLphp is not returning attributes? Try clearing Browser cookies and login one more time.