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): void11 {12 $email = $event->getEmail();13 $name = $event->getName();14 15 // Find or create user16 $user = User::firstOrCreate(17 ['email' => $email],18 ['name' => $name ?? $email]19 );20 21 // Authenticate user22 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 AD3</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 IDP10 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
Current version:
0.2.6