Intro
Single Sign-On (SSO) is a widely-used authentication method that makes applications more secure and reduces the attack surface for an organization. SAML (Security Assertion Markup Language) is a popular choice for implementing SSO and has been in use since the early 2000’s. Implementing SAML from scratch would be tricky for a beginner, but many great libraries have been written to streamline the work to set an integration up. In this blog post, I will show you how to add an SSO service provider integration to a Laravel application using the php-saml toolkit.
Service Providers vs Identity Providers
In this post, I will refer to the terms service provider (SP) and identity provider (IdP). These terms refer to the relationship between web applications in single sign-on.
An IdP is an application that verifies the identity of a user and checks what that user should have access to. This application could be Google, Active Directory or an IdP that you develop.
A service provider is an application that a user wants access to. This post will be focusing on building a service provider integration.
Prerequisites
Before we begin, ensure you have the following:
- Laravel application.
- Composer installed on your system.
- An account with an IdP that supports SAML (like OneLogin, Okta, Google, etc.) or a test IdP like SAMLTest.id.
Installing the Toolkit
First, we need to include the php-saml
library in our Laravel project. Run the following command in your project’s root directory:
composer require onelogin/php-saml
Configure SAML Settings
Make a new configuration file for your SAML settings at config/php-saml.php. This file will contain all settings for the toolkit, your SP and your IdP. Here’s a base to start from:
<?phpreturn [ 'strict' => true, // Enable debug mode (to print errors) 'debug' => false, // Service Provider settings 'sp' => [ 'entityId' => 'http://localhost:8000', 'assertionConsumerService' => [ 'url' => 'http://localhost:8000/saml/acs', 'binding' => 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST', ], 'NameIDFormat' => 'urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress', 'x509cert' => '<cert.pem contents here>', 'privateKey' => '<key.pem contents here>', ], // IdP settings 'idp' => [ 'entityId' => '<provided by IdP>', 'singleSignOnService' => [ 'url' => '<provided by IdP>', 'binding' => 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect', ], 'x509cert' => '<provided by IdP>', ], 'security' => [ 'authnRequestsSigned' => true, 'allowRepeatAttributeName' => true, ],];
Generating a Certificate Pair
An x.509 certificate is required for php-saml
. It will be used to generate cryptographic signatures to include within SAML requests. This helps the IdP verify that it is negotiating a login with the correct application.
Run this openssl
command in your terminal:
openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -sha256 -days 3650 -nodes -subj "/C=XX/ST=StateName/L=CityName/O=CompanyName/OU=CompanySectionName/CN=CommonNameOrHostname"
A pair of files will be generated: cert.pem
and key.pem
. Take the contents of cert.pem
and save it in the SP x509cert
setting. Do the same for key.pem
but save it in the privateKey
setting.
Note: While this tutorial has you add these keys to the configuration file for simplicity, it is highly recommended that you move these to environment variables or some manner of secret storage.
Configuring Your Identity Provider
Before the IdP settings can be filled out, additional setup is required in the admin panel of the IdP you are using. The method of getting your IdP set up will differ from provider to provider, but the process will generally follow the same steps:
- Create a new SAML 2.0 profile or application in the IdP
Provide values from your application to your IdP (You can find them within
config/php-saml.php
):- Entity ID
- Assertion Consumer Service URL
- x.509 certificate
Copy values from the IdP and save them in
config/php-saml.php
:- Entity ID
- Single Sign-On Service URL
- x.509 certificate
Set Up Routes
Define the necessary routes in routes/web.php
to handle login redirects and SAML responses:
Route::get('/saml/login', [SAMLController::class, 'login']);Route::post('/saml/acs', [SAMLController::class, 'acs'])->withoutMiddleware([\App\Http\Middleware\VerifyCsrfToken::class]);;
Create a SAML Controller
Generate a new controller named SAMLController
:
php artisan make:controller SAMLController
In this controller, we’ll handle redirecting users to the IdP and handling SAML responses from the IdP:
<?phpnamespace App\Http\Controllers;use App\Http\Controllers\Controller;use App\Models\User;use Illuminate\Http\Request;use OneLogin\Saml2\Auth;class SAMLController extends Controller{ public function login(Request $request) { $auth = new Auth(config('php-saml')); $redirectUrl = $auth->login(null, [], false, false, true); $request->session()->put('requestId', $auth->getLastRequestID()); return redirect($redirectUrl); } public function acs(Request $request) { $auth = new Auth(config('php-saml')); $auth->processResponse($request->get('requestId')); if (count($auth->getErrors()) > 0 || !$auth->isAuthenticated()) { return 'An error occurred processing SAML response'; } $user = User::query()->where('email', $auth->getNameId())->first(); if (!$user) { return 'User not found.'; } auth()->login($user); return redirect('/'); }}
In the acs
method of the controller, you’ll need to authenticate users with the data provided by the IdP. This typically involves checking if the user exists in your database and creating a session for them. While this sample code assumes that every user in the IdP should already be in the database, if you want to automatically provision a user, this PHP method would be the place to do it.
Time to Test!
When you’re ready, open up a new browser tab and navigate to http://localhost:8000/saml/login
. You should get automatically redirected to your IdP’s login page. After you login to the IdP, you should be redirected back to the SP /saml/acs
endpoint. If validation is successful, a user is logged in to the Laravel application and the browser will be redirected to the home page.
Conclusion
I hope you’ve now successfully integrated SAML 2.0 into a Laravel application using the php-saml
toolkit.
Remember to test your SSO integration thoroughly to ensure everything works as expected and that your application remains secure. Ensure that all SAML responses and requests are validated, handle errors appropriately, and keep your certificate credentials confidential.