Setup SAML2 with Laravel Acting as the Service Provider.

Joshua Callis
6 min readJan 22, 2021

--

NOTE: This isn’t an article to explain the ins and outs of SAML (as the post would be too large! I like to keep things to the point, if I can), it’s intended to show the general flow and how to set it up within Laravel.

I recently had a task where I was needed to integrate a provider with a service via SAML. I must confess, I’ve never had to use the rare technology! so I wasn’t confident!

Luckily, I’m very much involved in the PHP community and put the feelers out on who the HECK has done this before and can jump on a call to go through the flow.

Initially, I set out to set it up from scratch to learn it fully and to have our own service, yeah.. that was a terrible idea. Considering after further reading, it’s not likely to exist in the next 2–5 years within small to medium companies, as they become more innovative. In the future I predict only the enterprise companies will use it. But that’s where this guide comes in handy ;)

After a two hour call, I got the concept and started developing! So, for anyone wanting to setup a SAML2 integration within Laravel continue reading.

I need to quickly say a HUGE thank you to LEGENDS at 24slides! who created what I was originally going to do (purely for learning) https://github.com/24Slides/laravel-saml2 (I was in no way asked to share this information, they were just life savers!)

The steps.

  1. Install the laravel-saml2 package
  2. Run the migrations, as 24slides have setup the tables for the flow
  3. Ask the provider for their metadata, it’s usually a static xml file that contains what you need to add within the config to setup the tenant.
  4. The provider will also provide a certificate, you will need to extract the public and provider keys. I’m on mac so used openssl more on that later
  5. Extract the EntityId, LoginURL, LogoutURL (if provided) when creating the tenant from the identity providers metadata.
  6. laravel-saml2 will automatically generate your metadata for you. Provide that uri to the identity provider, this is the important part. In layman terms it says, I have your metadata and you have mine, great! we both know our metadata and the keys, so we can definitely trust each other.

My analogy to think about it was (bare with me!) you are seeing someone. You become official, you share your metadata ‘i.e interests, home location etc’ and when you trust each other. The partner gives you a key to access their house (i.e the identity providers certificate as you already know their metadata) and you accept that key and when you visit the house, you can use the key and they will recognise it’s you (i.e ‘you’ the service provider will be known to the identity provider and will have access).

Okay, now for the detailed part.

1. Install the laravel-saml2 package

composer require 24slides/laravel-saml2

2. Run the migrations

php artisan migrate

3. Ask the provider for their metadata, it’s usually a static xml file that contains what you need to add within the config to setup the consumer.

I’ve obfuscated the identity providers information. with ‘identity-provider’. This will be the uri of the identity provider you are integrating with.

As I didn’t know what ‘get the metadata’ from the identity provider meant until I seen it. It’s literally just a static file containing the info you need to extract, that’s it! Looks like this: https://github.com/jjcallis/identity-provider-metadata-example/blob/main/metadata.xml (example to show the example metadata clearly)

4. The provider will also provide you with a certificate, you will need to extract the public and provide keys. I’m on mac so used openssl.

Wrote a small blog on it here: https://joshuajordancallis.medium.com/extract-the-public-and-private-keys-to-pem-files-via-openssl-from-a-certificate-9716b7d470c6

5. Extract the EntityId, LoginURL, LogoutURL (if provided) when creating the tenant

Remember this is all available within the identity providers metadata

php artisan saml2:create-tenant \--key=identity-provider \--entityId=https://identity-provider.co.uk/Metadata/IdentityProviderIdPMetadata_v2.xml \--loginUrl=https://identity-provider/v2/SAML/ProcessAuthRequest.aspx \--logoutUrl=https://identity-provider/v2/SAML/ProcessLogoutRequest.aspx \--x509cert="ADD THE PUBLIC KEY CERT HERE AVAILABLE WITHIN THE PUBLIC KEY PEM FILE"

For more information on this read the documentation provided by 24Sliders i.e

In this case the identity provider doesn’t have a logoutURL so I provided a default. It’s required within the creation process, but it isn’t used.

Once you create the tenant, laravel-saml2 will automatically create the urls for you.

The tenant #3 (9a161af7-5eaa-497d-acd6-423a97b2bb98) was successfully created.Credentials for the tenant--------------------------Identifier (Entity ID): https://mywebsite.co.uk/saml2/9a161af7-5eaa-497d-acd6-423a97b2bb98/metadataReply URL (Assertion Consumer Service URL): https://mywebsite.co.uk/saml2/9a161af7-5eaa-497d-acd6-423a97b2bb98/acsSign on URL: https://mywebsite..co.uk/saml2/9a161af7-5eaa-497d-acd6-423a97b2bb98/loginLogout URL: https://mywebsite.co.uk/saml2/9a161af7-5eaa-497d-acd6-423a97b2bb98/logoutRelay State:  (optional)Joshuas-Mini:cashcalc joshuacallis$

Right…so what now?

  1. Send your metadata to the identity provider, so they can trust you ;) i.e https://mywebsite.co.uk/saml2/9a161af7-5eaa-497d-acd6-423a97b2bb98/metadata
  2. The identity provider also has to ‘trust your domain’ so once they have ‘whitelisted’ https://mywebsite.co.uk you can…
  3. Hit: https://mywebsite..co.uk/saml2/9a161af7-5eaa-497d-acd6-423a97b2bb98/login (within your app) this will go to the tenant you created and get the LoginURL, this will redirect your users to the identity providers front-end so your customer can log in..
  4. The identity provider will redirect to https://mywebsite..co.uk/saml2/9a161af7-5eaa-497d-acd6-423a97b2bb98/acs This is the rule where the identity provider will post the ‘response’ data for you to capture (see point 5 on how to capture the data).
  5. Within the EventServiceProvider in the boot method — easiest way to see the response, you can do what you like with the data then.
  6. FYI: an identity provider can post anything back! such as student information, financial information, car information… literally anything. It’s up-to the provider what they post in the `response` (you’re likely to already know what will be returned though)

Moving on with point 5: Capturing the data…

Add the listen Event, i.e so when the identity provider logs the customer in and redirects back to your application you can capture the event and receive the response. Pretty cool!

In my case, I’m going to capture the response and could use apache-kafka and push the info to a topic for a microservice to capture and save the data within the database and process the information. Then I will redirect the user off somewhere, likely the dashboard.

To note: this code was taken from the laravel-saml2 github example. Which is a little outdated, the getLastMessageId() and getSaml2User() mutator is within the auth (i.e the SAML user class) you can’t access it directly on the event, as the mutators are not available. So i’ve added $event->auth

Event::listen(\Slides\Saml2\Events\SignedIn::class, function (\Slides\Saml2\Events\SignedIn $event) {$messageId = $event->auth->getLastMessageId();// your own code preventing reuse of a $messageId to stop replay attacks$samlUser = $event->auth->getSaml2User();$userData = ['id' => $samlUser->getUserId(),'attributes' => $samlUser->getAttributes(),'assertion' => $samlUser->getRawSamlAssertion()];dd($userData);// Login a user.Auth::login($user);});

Let should be it!

You’ve got their metadata and they’ve got yours, you have their certificate so they can trust you and they have the uri to post the ‘response data’ back to - for you to capture and do what you like with it.

NOTE: To populate your info within the metadata, you need to fill out these ENV variables. This isn’t within laravel-saml2 documentation, but I found it from reading/understanding there code/package — which I recommend doing, as it’s no good in using something if you don’t really know what’s happening!

SAML2_SP_CERT_x509="" // the public key.SAML2_SP_CERT_PRIVATEKEY="location/to/private/key/on/app/privatekey.pem" // I generally put them within /storage/appSAML2_CONTACT_TECHNICAL_NAME="development"SAML2_CONTACT_TECHNICAL_EMAIL="developer@example.co.uk"SAML2_CONTACT_SUPPORT_NAME="support"SAML2_CONTACT_SUPPORT_EMAIL="support@example.co.uk"SAML2_ORGANIZATION_NAME="Example"SAML2_ORGANIZATION_URL="https://example.co.uk"

--

--

Joshua Callis
Joshua Callis

Written by Joshua Callis

Converted DevOps Engineer at oso.sh, Previously a Senior Software Engineer.

Responses (3)