Introduce
These are memos when using Single sign-on using SAML for Laravel 5.
Most of the content is similar to README of laravel-saml2.
Environment
Works from Laravel 5.4 and later.
Setting
Install laravel-saml2
composer require aacotroneo/laravel-saml2
For versions of Laravel before 5.5, it is necessary to add a Service Provider to configapp.php
as shown below:
config app.php
1 2 3 4 5 6 7 8 9 10 | 'providers' => [ ... AacotroneoSaml2Saml2ServiceProvider::class, ] 'alias' => [ ... 'Saml2' => AacotroneoSaml2FacadesSaml2Auth::class, ] |
Implement vendor: publish php artisan vendor:publish --provider="AacotroneoSaml2Saml2ServiceProvider"
appconfigsaml2_settings.php
will be created.
Create Laravel authenticataion
Create Auth for Laravel
1 2 3 | php artisan make:auth php artisan migrate |
Install SAML
Register metadata into Idp
Confirm the Laravel metadata SAML (SAML SP) by following the link below, then register on the IdP side.
http://localhost/Laravel-app-name/public/saml2/metadata
Set up the IdP metadata for Laravel
Set up the IdP metadata into saml2_settings.php
of Laravel.
Specify the environment variables entityId, singleSignOnService, singleLogoutService, x509cert.
This site is using Google IdP.
app config saml2_settings.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | // Identity Provider Data that we want connect with our SP 'idp' => array( // Identifier of the IdP entity (must be a URI) 'entityId' => 'https://accounts.google.com/o/saml2?idpid=****', // SSO endpoint info of the IdP. (Authentication Request protocol) 'singleSignOnService' => array( // URL Target of the IdP where the SP will send the Authentication Request Message, // using HTTP-Redirect binding. 'url' => 'https://accounts.google.com/o/saml2/idp?idpid=****', ), // SLO endpoint info of the IdP. 'singleLogoutService' => array( // URL Location of the IdP where the SP will send the SLO Request, // using HTTP-Redirect binding. 'url' => 'https://accounts.google.com/Logout', ), // Public x509 certificate of the IdP 'x509cert' => '****', /* * Instead of use the whole x509cert you can use a fingerprint * (openssl x509 -noout -fingerprint -in "idp.crt" to generate it) */ // 'certFingerprint' => '', |
Change routesMiddleware
Because the Login session has not been created when adding VerifyCsrfToken, we have to change the routesMiddleware of saml2_settings.php
as shown below:
app config saml2_settings.php 'routesMiddleware' => ['saml'],
Add new SAML Middle ware group to Kernel.php.
The content of the saml group after removing VerifyCsrfToken from the web group.
app Http Kernel.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | protected $middlewareGroups = [ 'web' => [ AppHttpMiddlewareEncryptCookies::class, IlluminateCookieMiddlewareAddQueuedCookiesToResponse::class, IlluminateSessionMiddlewareStartSession::class, // IlluminateSessionMiddlewareAuthenticateSession::class, IlluminateViewMiddlewareShareErrorsFromSession::class, AppHttpMiddlewareVerifyCsrfToken::class, IlluminateRoutingMiddlewareSubstituteBindings::class, ], 'api' => [ 'throttle:60,1', 'bindings', ], // samlグループを追加 'saml' => [ AppHttpMiddlewareEncryptCookies::class, IlluminateCookieMiddlewareAddQueuedCookiesToResponse::class, IlluminateSessionMiddlewareStartSession::class, IlluminateViewMiddlewareShareErrorsFromSession::class, IlluminateRoutingMiddlewareSubstituteBindings::class, ], ]; |
Create the authentication section
Create Event listeners for SAML login and logout
Describe the event when SAML login and logout.
Because of the association with Laravel’s Auth, the following will be done:
- When SAML login, Auth also login
- When SAML logout, Auth also logout
Add boot method to EventServiceProvider.php
app Providers EventServiceProvider.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | Event::listen('AacotroneoSaml2EventsSaml2LoginEvent', function (Saml2LoginEvent $event) { $messageId = $event->getSaml2Auth()->getLastMessageId(); // your own code preventing reuse of a $messageId to stop replay attacks $user = $event->getSaml2User(); // 属性からUserモデルを取得する $userData = [ 'id' => $user->getUserId(), 'attributes' => $user->getAttributes(), 'assertion' => $user->getRawSamlAssertion() ]; $laravelUser = AppUser::where('email', $userData['attributes']['emailAddress'])->first(); //if it does not exist create it and go on or show an error message if ($laravelUser) { Auth::login($laravelUser); } else { abort(401, 'Authorization Required'); } }); Event::listen('AacotroneoSaml2EventsSaml2LogoutEvent', function ($event) { Auth::logout(); Session::save(); }); |
Synchronize the properties obtained with the environment. If a 401 error occurs when validating then use the corresponding 401 view as the link below: appresourcesviewserrors401.blade.php
Create Middleware SAML authentication
Create Middleware to authenticate SAML. By using this Middleware, a SAML login is required to access the pages.
Use artisan command to create Middleware named SamlAuth.
php artisan make:middleware SamlAuth
Edit SamlAuth.php as follows:
app Http Middleware SamlAuth.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | <?php namespace AppHttpMiddleware; use Closure; use IlluminateSupportFacadesAuth; use IlluminateSupportFacadesURL; use AacotroneoSaml2FacadesSaml2Auth; class SamlAuth { /** * Handle an incoming request. * * @param IlluminateHttpRequest $request * @param Closure $next * @return mixed */ public function handle($request, Closure $next) { // SAMLログイン認証 if (Auth::guest()) { if ($request->ajax()) { return response('Unauthorized.', 401); } else { Saml2Auth::login(URL::full()); } } return $next($request); } } |
Register SamlAuth in Kernel.php’s routeMiddleware.
app Http Kernel.php
1 2 3 4 5 6 7 8 9 10 11 | protected $routeMiddleware = [ 'auth' => IlluminateAuthMiddlewareAuthenticate::class, 'auth.basic' => IlluminateAuthMiddlewareAuthenticateWithBasicAuth::class, 'bindings' => IlluminateRoutingMiddlewareSubstituteBindings::class, 'can' => IlluminateAuthMiddlewareAuthorize::class, 'guest' => AppHttpMiddlewareRedirectIfAuthenticated::class, 'throttle' => IlluminateRoutingMiddlewareThrottleRequests::class, // 追加 'samlauth' => AppHttpMiddlewareSamlAuth::class, ]; |
Use authentication
Use Middleware authentication.
Below is an example of using middleware for a route group.
1 2 3 4 | Route::middleware(['samlauth'])->group(function () { Route::get('/home', ' <a class="__cf_email__" href="/cdn-cgi/l/email-protection">[email protected]</a> '); }); |
Check activity
Create data user with email and IdP in table Users.
Access to / home, will appear IdP login screen.
Proceed to login by email address.