Beartropy SAML2

Login Listener
Configure authentication handling after a successful SAML login and integrate SAML routes with your application.

Publish Login Listener

The listener is essential to handle what happens after a successful SAML authentication.

1php artisan saml2:publish-listener

Default Listener

This creates app/Listeners/HandleSaml2Login.php:

1 
2namespace App\Listeners;
3 
4use Beartropy\Saml2\Events\Saml2LoginEvent;
5use Illuminate\Support\Facades\Auth;
6use App\Models\User;
7 
8class HandleSaml2Login
9{
10 public function handle(Saml2LoginEvent $event): void
11 {
12 $email = $event->getEmail();
13 $name = $event->getName();
14 
15 // Find or create user
16 $user = User::firstOrCreate(
17 ['email' => $email],
18 ['name' => $name ?? $email]
19 );
20 
21 // Authenticate user
22 Auth::login($user, remember: true);
23 }
24}

Available Event Methods

The Saml2LoginEvent provides several helper methods to access SAML response data:

Method Return Description
getEmail() ?string Get email from common attribute sources (mapped, raw, or NameID)
getName() ?string Get name from common attribute sources (displayName, cn)
getAttribute($key, $default) mixed Get a specific mapped attribute by key
getAttributes() array Get all mapped attributes
getRawAttribute($key, $default) mixed Get a specific raw SAML attribute by key
getRawAttributes() array Get all raw SAML attributes as received
toArray() array Get all event data as an array

Public Properties

Property Type Description
$idpKey string The key of the IDP that authenticated the user
$nameId string The SAML NameID (usually email or unique identifier)
$attributes array Mapped attributes based on config
$rawAttributes array Raw SAML attributes as received from IDP
$sessionIndex ?string The SAML session index for Single Logout (SLO)

Customization Examples

Sync Roles from SAML

Sync roles using spatie/laravel-permission:

1public function handle(Saml2LoginEvent $event): void
2{
3 $email = $event->getEmail();
4 $name = $event->getName();
5 $groups = $event->getAttribute('groups') ?? [];
6 
7 $user = User::firstOrCreate(
8 ['email' => $email],
9 ['name' => $name ?? $email]
10 );
11 
12 // Sync roles (spatie/laravel-permission)
13 if (!empty($groups)) {
14 $user->syncRoles($groups);
15 }
16 
17 Auth::login($user, remember: true);
18}

Update User Data on Every Login

1public function handle(Saml2LoginEvent $event): void
2{
3 $user = User::updateOrCreate(
4 ['email' => $event->getEmail()],
5 [
6 'name' => $event->getName(),
7 'first_name' => $event->getAttribute('firstname'),
8 'last_name' => $event->getAttribute('lastname'),
9 'last_login_at' => now(),
10 ]
11 );
12 
13 Auth::login($user, remember: true);
14}

Validate Email Domain

1public function handle(Saml2LoginEvent $event): void
2{
3 $email = $event->getEmail();
4 
5 // Validate domain
6 if (!str_ends_with($email, '@your-company.com')) {
7 throw new \Exception('Email domain not allowed');
8 }
9 
10 $user = User::firstOrCreate(
11 ['email' => $email],
12 ['name' => $event->getName()]
13 );
14 
15 Auth::login($user, remember: true);
16}

Integrate SAML with Your Routes

Simple Login Link

1<a href="{{ route('saml2.login', ['idp' => 'azure']) }}">
2 Login with Azure AD
3</a>

Multiple IDPs

1<h3>Choose your identity provider:</h3>
2<ul>
3 <li><a href="{{ route('saml2.login', ['idp' => 'azure']) }}">Azure AD</a></li>
4 <li><a href="{{ route('saml2.login', ['idp' => 'okta']) }}">Okta</a></li>
5 <li><a href="{{ route('saml2.login', ['idp' => 'adfs']) }}">Corporate ADFS</a></li>
6</ul>

Replacing Default Auth Routes

Example: Use local auth in development, SAML in production:

1// routes/auth.php
2 
3Route::middleware('guest')->group(function () {
4 if (app()->environment('local')) {
5 // Local login for development
6 Route::get('/login', [AuthController::class, 'showLogin'])->name('login');
7 Route::post('/login', [AuthController::class, 'authenticate']);
8 } else {
9 // SAML Login - redirects to IDP
10 Route::get('/login', function () {
11 return redirect()->route('saml2.login', ['idp' => 'azure']);
12 })->name('login');
13 }
14});
15 
16Route::middleware('auth')->group(function () {
17 if (app()->environment('local')) {
18 Route::post('/logout', [AuthController::class, 'logout'])->name('logout');
19 } else {
20 // SAML Logout (SLO)
21 Route::match(['get', 'post'], '/logout', function () {
22 return redirect()->route('saml2.logout');
23 })->name('logout');
24 }
25});

Post-Setup Checklist

  • SP Entity ID configured in .env
  • IDP created and active
  • Login listener published and customized
  • Login/logout routes integrated into your app
  • SP certificates generated (production)
  • Admin middleware configured (optional)
  • Attribute mapping configured (if needed)
  • Tested complete login flow
Beartropy Logo

© 2026 Beartropy. All rights reserved.

Provided as-is, without warranty. Use at your own risk.