Authentication is one of the most common features in any web development project. Therefore, Drupal, as one of the most popular CMS out there, has their own implementation and opinions about how authentication should be implemented.
However, what happens when there are multiple sites under the same domain/subdomain and there is the need for one single login for all them, Do you have to tell your users to create multiple user accounts on each app/website?
What if the app stack is made with different frameworks or there are other projects linked to your app/website, Does that mean the dev team will have to deal with their own opinionated authentication process as well?
If you are asking yourself those questions, the answer may be that you need a single sign-on service for your applications.
There are many services out which will give you all the tools you need to have one single login across all your application stack. Nevertheless, this blog post will focus on describing how to have one single login between two Drupal sites using the SAML open standard.
Here is our stack
Drupal 9 websites
We will need two Drupal 9 websites, the Identity Provider or IdP and the Service Provider or SP.
These Drupal sites will be built with the composer Recommended Project from Drupal:
composer create-project drupal/recommended-project my_site_name_dir
More about Drupal and Composer here: https://www.drupal.org/docs/develop/using-composer/using-composer-to-install-drupal-and-manage-dependencies
Lando
This is a great project that uses Docker for creating containers with all the necessary services to run any Drupal application.
The great thing about Lando and/or Docker is that help us with a dev stack for stateless applications. In other words, we will be able to spin off new versions of the site very easily without having to configure or making complex changes in our OS.
Simplesamlphp
This is the star of the show!
Simplesamlphp is a PHP library that implements the SAML (Security Assertion Markup Language) open standard.
We will be looking into more details further down the road, but for now, implementing this library will give us that single login for all the applications in our stack.
Last but not least
We will be also using these two fantastic projects:
- Drupalauth: This is a module for the Simplesaml library which will connect Simplesaml with the Drupal user table.
- DrupalAuth for SimpleSAMLphp: This is a Drupal module which will handle the connection between Drupal and Simplesaml.
What does IDP and SP even mean?
These two concepts are the key components in a SMAL authentication process.
Identity provider
A SAML identity provider is a system entity that issues authentication assertions in conjunction with a single sign-on (SSO) profile of the Security Assertion Markup Language (SAML).
In the SAML domain model, a SAML authority is any system entity that issues SAML assertions.[OS 1] Two important examples of SAML authorities are the authentication authority and the attribute authority.
Extracted from: wikipedia.org
Service provider
A SAML service provider is a system entity that receives and accepts authentication assertions in conjunction with a single sign-on (SSO) profile of the Security Assertion Markup Language (SAML).
In the SAML domain model, a SAML relying party is any system entity that receives and accepts information from another system entity.[OS 1] Of particular interest is a SAML relying party that receives and accepts a SAML assertion issued by a SAML authority.
Extracted from: wikipedia.org
What does that mean for our project?
In our stack, the IdP will be our main Drupal site where all the usernames and passwords will be stored. In other words, think of this site as the master site where all the other apps will be connected to in other to login.
On the other hand, we have the SP, another Drupal 9 site, this site won't store any user data and, when users click on login, their login credentials (username and password) will be verified against the IdP.
Enough theory, let's do this
I have created two GitHub repositories one for the IDP and another for the SP:
The IDP
https://github.com/esnaremaussa/drupal9-idp
The SP
https://github.com/esnaremaussa/drupal9-sp
Those repositories already include a .lando.yml file. Therefore, running lando start will spin off the Docker containers with the LAMP stack and any other fancy configuration for Drupal.
You will end up with two sites:
NOTE: We will use Lando throughout this blog post, so I recommend to install it if you want to follow along.
Configuring the IdP website
It's the moment for the drupalauth and DrupalAuth for SimpleSAMLphp projects to shine.
As per the Drupalauth project documentation, there are two ways to do this:
- Authenticate against Drupal but use the SimpleSAMLphp login page
- Authenticate against Drupal but use the Drupal login page
We will be working with the 2nd option.
Installing the Simplesamlphp library
We will run the following command on the terminal:
# Downloding the drupalauth4ssp module
$ lando composer require drupal/drupalauth4ssp
# Installing the drupalauth4ssp module
$ lando drush en drupalauth4ssp -y
With the above command, Composer will download all the third-party libraries we need for this project. Let me explain, the Drupalauth4ssp module has listed the Simplesamlphp library as well as Drupalauth module as a dependency. Therefore, when Composer is downloading the Drupalauth4ssp, it will also download all the libraries we require to get Simplesaml working on our project.
On top of that, Composer will take care of versioning. In other words, it will make sure that the right versions are downloaded for this project.
You got to love composer!
In order to double-check that everything was installed correctly, go to your vendor folder and look for the simplesamlphp project, then check inside the modules folder, the Drupalauth module should be there too.
# Check if the simplesamlphp library is installed
ls -lha vendor/simplesamlphp/simplesamlphp
# Check if the Drupalauth module is installed
ls -lha vendor/simplesamlphp/simplesamlphp/modules/drupalauth
Configuring the simplesamlphp library
At the moment, the Simplesamlphp library is downloaded but we can't access it from the browser. The reason being, Drupal only allows specific folders to be accessible by Apache/Nginx. Let's fix this by creating a symlink from the www folder, inside the simplesamlphp folder, to the web folder.
# Connect inside the appserver container
lando ssh
# Create the symlink
ln -nfs /app/vendor/simplesamlphp/simplesamlphp/www web/simplesaml
.htaccess configuration
All directories on Drupal are protected by a configuration on the .htaccess file. Therefore, we need to whitelist Simplesmal directories so we can see it on the browser.
Just after this line:
RewriteCond %{REQUEST_URI} !/core/modules/statistics/statistics.php$
Add the code:
# Allow access to simplesamlphp scripts.
RewriteCond %{REQUEST_URI} !/simplesaml/[^/]*\.php$
RewriteCond %{REQUEST_URI} !/simplesaml/admin/[^/]*\.php$
RewriteCond %{REQUEST_URI} !/simplesaml/[^/]*\.php/sanitycheck/[^/]*\.php$
RewriteCond %{REQUEST_URI} !/simplesaml/[^/]*\.php/drupalauth/[^/]*\.php$
RewriteCond %{REQUEST_URI} !/simplesaml/[^/]*\.php/core/idp/[^/]*\.php$
RewriteCond %{REQUEST_URI} !/simplesaml/[^/]*\.php/saml/[^/]*\.php$
RewriteCond %{REQUEST_URI} !/simplesaml/saml2/idp/[^/]*\.php$
RewriteCond %{REQUEST_URI} !/simplesaml/saml2/idp/[^/]*\.php/drupal-userpass$
RewriteCond %{REQUEST_URI} !/simplesaml/[^/]*\.php/saml/sp/[^/]*\.php/default-sp$
Although You should be able to access the Simplesaml from the browser now, you may be facing an error. This issues will be solved in the next step.
IMPORTANT: From simplicity, I will be modifying configuration files from the vendor folder. However, this is not recommended and I will encourage you to look for the best way to persist the data for your configuration and deployment process.
Config Files and Metadata
Currently, the IdP website is displaying some errors when accessing the simplesaml library at https://idp.lndo.site/simplesaml/
Let's fix this by copy the configuration files from the config-templates to the config folder:
cp -r vendor/simplesamlphp/simplesamlphp/config-templates/* vendor/simplesamlphp/simplesamlphp/config
And let's the same for the metadata files
cp -r vendor/simplesamlphp/simplesamlphp/metadata-templates/* vendor/simplesamlphp/simplesamlphp/metadata
Now, let's dive deep into the Simplesamlphp configuration.
look for the config.php file inside the config folder and do the following changes:
'secretsalt' => 'RANDOM-NUMBER',
Running this command on the terminal will give you a random number for the secret salt:
LC_CTYPE=C tr -c -d '0123456789abcdefghijklmnopqrstuvwxyz' </dev/urandom | dd bs=32 count=1 2>/dev/null;echo
Master password for the Simplesamlphp backend:
'auth.adminpassword' => 'MySuperStrongPassword',
You can use the story type you'd like, for this example, we will use MySQL:
'store.type' => 'sql'
'store.sql.dsn' => 'mysql:host=database;dbname=drupal9',
'store.sql.username' => 'drupal9',
'store.sql.password' => 'drupal9',
Enable the IdP mode:
'enable.saml20-idp' => true,
Finally, add this line add the end of the config.php file:
$config['baseurlpath'] = 'https://'. $_SERVER['HTTP_HOST'] . '/simplesaml/';
Certificates
Simplesaml requires certificates to be generate on the server for more security. A new cert folder will have to be created and generate the certificates there:
# Connect inside the appserver container
$ lando ssh
# Create cert folder
$ mkdir -p vendor/simplesamlphp/simplesamlphp/cert
# Go to the cert folder
$ cd vendor/simplesamlphp/simplesamlphp/cert
# Generate certificates
$ openssl req -newkey rsa:3072 -new -x509 -days 3652 -nodes -out server.crt -keyout server.pem
simplesamlphp-module-drupalauth configuration
The Drupalauth project is a Simplesamlphp module which connects Simplesamlphp with the IdP website. In addition, it maps the Drupal user credentials in a way that Simplesamlphp can understand.
NOTE: As stated at the beginning, we will be configuring the IdP site so it Authenticates against Drupal but use the Drupal login page.
In the configuration folder, look for the **authsources.php file, then copy and paste the following code:
'drupal-userpass' => [
'drupalauth:External',
// The filesystem path of the Drupal directory.
'drupalroot' => '/app/web/',
// Whether to turn on debug
'debug' => true,
// the URL of the Drupal logout page
'drupal_logout_url' => 'https://idp.lndo.site/user/logout',
// the URL of the Drupal login page
'drupal_login_url' => 'https://idp.lndo.site/user/login',
// Which attributes should be retrieved from the Drupal site.
'attributes' => array(
array('field_name' => 'uid', 'attribute_name' => 'uid'),
array('field_name' => 'roles', 'attribute_name' => 'roles', 'field_property' => 'target_id'),
array('field_name' => 'name', 'attribute_name' => 'cn'),
array('field_name' => 'mail', 'attribute_name' => 'mail'),
array('field_name' => 'field_first_name', 'attribute_name' => 'givenName'),
array('field_name' => 'field_last_name', 'attribute_name' => 'sn'),
array('field_name' => 'field_organization', 'attribute_name' => 'ou', 'field_property' => 'target_id'),
),
],
Metadata configuration
For this section, we will be modifying the files inside the metadata folder:
ls vendor/simplesamlphp/simplesamlphp/metadata
At the moment, we just need to locate the saml20-idp-hosted.php and the drupal-userpass value on the auth key.
'auth' => 'drupal-userpass',
There will more metadata we need to configure but let's create the SP website first.
Testing your work so far
There is a very simple way to know if you have done the steps above properly. First, go to the Simplesamlphp backend, https://idp.lndo.site/simplesaml. Then, go to the Test authentication sources section. Finally, click on the drupal-userpass option listed there.After you click it, you should be taken to the Drupal login page. That means you're on track! ;)
Configuring the Service Provider (SP)
Let's move on now and create a consumer website. We'll be using the https://github.com/esnaremaussa/drupal9-sp repository to build this project.
Run lando start and you should be running your site at https://sp.lndo.site
Next, download and install simpleSAMLphp Authentication module from Drupal.
# Download the simplesamlphp_auth Authentication module
$ lando composer require drupal/simplesamlphp_auth
# Install the simplesamlphp_auth Authentication module
$ lando drush en simplesamlphp_auth -y
The simplesamlphp_auth has the Simplesamlphp library as a dependency. Therefore, you should be able to find it in your vendor folder after running the composer install command.
Configuring the simplesamlphp_auth module
This module is very flexible and offers a good set of configuration for your Drupal site. However, the module is disabled by default.
For this example, we will make minimal changes to this module. Thus, go to the User info and syncing tab and add the value uid for the following fields:
- SimpleSAMLphp attribute to be used as unique identifier for the user.
- SimpleSAMLphp attribute to be used as username for the user.
Configuring the Simplesamlphp library for the SP
The Simplesamlphp library is a dependency for the simplesamlphp_auth module. Therefore, it should be downloaded in the vendor folder.
# Check if the simplesamlphp library is installed
$ ls -lha vendor/simplesamlphp/simplesamlphp
The configuration steps for the SP are pretty much the same as the IdP. Thus here is a quick summary of the commands for configuring the Simplesamlphp library:
# SSH the appserver container
lando ssh
# Create the symlink
ln -nfs /app/vendor/simplesamlphp/simplesamlphp/www web/simplesaml
.httaccess configuration
Just after this line:
RewriteCond %{REQUEST_URI} !/core/modules/statistics/statistics.php$
Add the code:
# Allow access to simplesamlphp scripts.
RewriteCond %{REQUEST_URI} !/simplesaml/[^/]*\.php$
RewriteCond %{REQUEST_URI} !/simplesaml/admin/[^/]*\.php$
RewriteCond %{REQUEST_URI} !/simplesaml/[^/]*\.php/sanitycheck/[^/]*\.php$
RewriteCond %{REQUEST_URI} !/simplesaml/[^/]*\.php/drupalauth/[^/]*\.php$
RewriteCond %{REQUEST_URI} !/simplesaml/[^/]*\.php/core/idp/[^/]*\.php$
RewriteCond %{REQUEST_URI} !/simplesaml/[^/]*\.php/saml/[^/]*\.php$
RewriteCond %{REQUEST_URI} !/simplesaml/saml2/idp/[^/]*\.php$
RewriteCond %{REQUEST_URI} !/simplesaml/saml2/idp/[^/]*\.php/drupal-userpass$
RewriteCond %{REQUEST_URI} !/simplesaml/[^/]*\.php/saml/sp/[^/]*\.php/default-sp$
Config Files and Metadata
Add the code:
# Copy configuration template files
cp -r vendor/simplesamlphp/simplesamlphp/config-templates/* vendor/simplesamlphp/simplesamlphp/config
# Copy metadata template files
cp -r vendor/simplesamlphp/simplesamlphp/metadata-templates/* vendor/simplesamlphp/simplesamlphp/metadata
Making changes on the config.php file
'secretsalt' => 'RANDOM-NUMBER',
'auth.adminpassword' => 'MySuperStrongPassword',
'store.type' => 'sql'
'store.sql.dsn' => 'mysql:host=database;dbname=drupal9',
'store.sql.username' => 'drupal9',
'store.sql.password' => 'drupal9',
#Finally, add this line at the end of the config.php file:
$config['baseurlpath'] = 'https://'. $_SERVER['HTTP_HOST'] . '/simplesaml/';
Making changes on the authsources.php file
For the SP, we will use the default-sp authentication source.
Checkout the code snipped:
'idp' => 'https://idp.lndo.site/simplesaml/saml2/idp/metadata.php',
Certificates
# SSH the appserver container
$ lando ssh
# Create cert folder
$ mkdir -p vendor/simplesamlphp/simplesamlphp/cert
# Go to the cert folder
$ cd vendor/simplesamlphp/simplesamlphp/cert
# Generate certificates
$ openssl req -newkey rsa:3072 -new -x509 -days 3652 -nodes -out server.crt -keyout server.pem
Metadata SP configuration
Open the file saml20-idp-hosted.php, inside the metadata folder and add the following code:
'auth' => 'drupal-userpass',
Exchanging Metadata between the SP and Idp
This is the final step!
Let's add the SP metadata on the IdP
On your SP Simplesaml website, go to the Federation tab, look for the SAML 2.0 SP Metadata section and click on show metadata.
You should copy the PHP array on the metadata section.
If you have been following along, this should be SP metadata URL: https://sp.lndo.site/simplesaml/module.php/saml/sp/metadata.php/default-sp?output=xhtml
Then, go to your IdP codebase, look for the metadata folder and locate the saml20-sp-remote.php file, at the end of that file you should paste the PHP array.
# This is an example of the first lines of the metadata array.
$metadata['https://sp.lndo.site/simplesaml/module.php/saml/sp/metadata.php/default-sp'] = array (
'SingleLogoutService' =>
array (
0 =>
array (
'Binding' => 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect',
'Location' => 'https://sp.lndo.site/simplesaml/module.php/saml/sp/saml2-logout.php/default-sp',
),
.
.
.
Let's add the IdP metadata on the SP
On your IdP Simplesaml website, go to the Federation tab, look for the SAML 2.0 IdP Metadata section and click on show metadata.
If you have been following along, this should be IdP metadata URL: https://idp.lndo.site/simplesaml/saml2/idp/metadata.php?output=xhtml
Then, go to your SP codebase, look for the metadata folder and locate the saml20-idp-remote.php file, at the end of that file you should paste the PHP array.
# This is an example of the first lines of the metadata array.
$metadata['https://idp.lndo.site/simplesaml/saml2/idp/metadata.php'] = array (
'metadata-set' => 'saml20-idp-remote',
'entityid' => 'https://idp.lndo.site/simplesaml/saml2/idp/metadata.php',
'SingleSignOnService' =>
.
.
.
Known issues
Currently, there is just one issue with the Drupalauth Simplesamlphp module. The Crypt::hashEquals function is deprecated for Drupal 9 and the hash_equals from PHP should be used instead.
Have a look here: https://github.com/drupalauth/simplesamlphp-module-drupalauth/issues/62
Wrapping Up
And there you are!
You should have now the Simplesamlphp library up and running on both SP and IdP sites. Also, you should be able to authenticate on the SP with the users registered on the IdP.
Statesless version
Lando not only provides the option of spin-off environments for Drupal development. It also allows to run configuration code before/after the services are up and running. This is possible because Lando is a superset of Docker and you should be able to do the same things that you normally do with Docker Compose.
In other words, all those commands needed for the Simplesamlphp library configuration can be added to the IdP and SP websites before the app services are running, just like a CI/CD system.
Go to any of these two projects:
There should be a branch called stateless. In that branch, all the configuration explained here will be done automatically just by running lando start.
Top comments (4)
Hi,
I am trying to make an install where a Drupal instance works as IdP with another D9 instance working as SP.
Drupal version : 9.1.5
DrupalAuth version 8.x-1.1
The configuration is OK, but upon login :
i go to SP website click on Federated Login, which brings me to IDP website
i enter username & password and i get the following message :
The website encountered an unexpected error. Please try again later.
Error: Call to a member function setFormClass() on null in field_ui_entity_type_build() (line 74 of ore/modules/field_ui/field_ui.module).
field_ui_entity_type_build(Array) (Line: 129)
Drupal\Core\Entity\EntityTypeManager->findDefinitions() (Line: 175)
Drupal\Core\Plugin\DefaultPluginManager->getDefinitions() (Line: 83)
Drupal\Core\Entity\EntityTypeRepository->getEntityTypeFromClass('Drupal\user\Entity\User') (Line: 487)
Drupal\Core\Entity\EntityBase::load('2') (Line: 156)
SimpleSAML\Module\drupalauth\Auth\Source\External->getUser() (Line: 176)
SimpleSAML\Module\drupalauth\Auth\Source\External->authenticate(Array) (Line: 210)
SimpleSAML\Auth\Source->initLogin(Array, NULL, Array) (Line: 169)
SimpleSAML\Auth\Simple->login(Array) (Line: 357)
SimpleSAML\IdP->authenticate(Array) (Line: 415)
SimpleSAML\IdP->handleAuthenticationRequest(Array) (Line: 492)
SimpleSAML\Module\saml\IdP\SAML2::receiveAuthnRequest(Object) (Line: 26)
I went through the installation process + config multiple times.
Thank you in advance for your help/guidance.
Please share fix you applied
Do not disable discovery cache
github.com/drupalauth/simplesamlph...
Hi @esnare and @tarxor
I am facing same issue and error as mentioned below when trying to login to IDP Drupal
Please suggest solution