beartropy / saml2
Multi-IDP SAML2 SP integration for Laravel using onelogin/php-saml
Installs: 25
Dependents: 0
Suggesters: 0
Security: 0
Stars: 0
Watchers: 0
Forks: 0
Open Issues: 0
pkg:composer/beartropy/saml2
Requires
- php: ^8.2
- illuminate/database: ^11.0|^12.0
- illuminate/routing: ^11.0|^12.0
- illuminate/support: ^11.0|^12.0
- onelogin/php-saml: ^4.3
Requires (Dev)
- orchestra/testbench: ^9.0|^10.0
- pestphp/pest: ^3.0
- pestphp/pest-plugin-laravel: ^3.0
README
🛡️ Beartropy SAML2
Multi-IDP SAML2 Service Provider integration for Laravel using onelogin/php-saml
Integrate your Laravel app with multiple Identity Providers (Azure AD, Okta, ADFS, etc.)
Features
- 🔐 Configure your Laravel app as a SAML 2.0 Service Provider
- 🏢 Support for multiple Identity Providers (Azure AD, Okta, ADFS, etc.)
- 🖥️ Web UI for first-deploy setup and admin management
- 📦 IDP configuration from environment variables or database
- 🔄 Automatic metadata import from IDP URLs
- 🎯 Event-driven authentication (handle login your way)
- ⚙️ Artisan commands for IDP management
- 🌐 English and Spanish translations included
📚 Documentation
👉 Read the full documentation at beartropy.com/saml2
Installation
composer require beartropy/saml2
Publish the configuration and run migrations:
php artisan vendor:publish --tag=beartropy-saml2-config php artisan migrate
Quick Start
Option A: Using the Setup Wizard (Recommended)
After installation, simply navigate to:
https://your-app.com/saml2/setup
The First-Deploy Setup Wizard will guide you through:
- Displaying your SP metadata (Entity ID, ACS URL, Metadata URL) to share with your IDP administrator
- Configuring your IDP via:
- Metadata URL - Fetch and parse automatically
- Paste XML - Copy/paste metadata from your IDP
- Manual Entry - Enter Entity ID, SSO URL, Certificate manually
Note: The setup wizard is only accessible before the first IDP is configured. After that, use the Admin Panel.
Option B: Using Artisan Commands
1. Configure your Service Provider
Add to your .env:
SAML2_SP_ENTITY_ID=https://your-app.com
For signed assertions (recommended), generate SP certificates:
php artisan saml2:generate-cert
2. Add an Identity Provider
From a metadata URL:
php artisan saml2:create-idp azure --from-url=https://login.microsoftonline.com/.../federationmetadata.xml
Or interactively:
php artisan saml2:create-idp azure --interactive
Admin Panel
Once configured, manage your SAML2 settings through the web-based admin panel:
https://your-app.com/saml2/admin
Features
| Feature | Description |
|---|---|
| Dashboard | View SP metadata and list all configured IDPs |
| Create IDP | Add new Identity Providers with metadata import |
| Edit IDP | Modify IDP settings (Entity ID, SSO URL, Certificate, etc.) |
| Attribute Mapping | Configure per-IDP attribute mapping or use global config |
| Toggle Status | Activate/deactivate IDPs without deleting |
| Refresh Metadata | Auto-update IDP settings from metadata URL |
| Delete IDP | Remove an IDP from database |
Admin Configuration
Customize the admin panel in config/beartropy-saml2.php:
// Enable/disable admin panel 'admin_enabled' => env('SAML2_ADMIN_ENABLED', true), // Route prefix (default: /saml2/admin) 'admin_route_prefix' => env('SAML2_ADMIN_PREFIX', 'saml2/admin'), // Middleware to protect admin routes 'admin_middleware' => ['web', 'auth'], // Add 'admin' or custom middleware as needed
Protecting Admin Routes
Add your own authorization middleware:
// config/beartropy-saml2.php 'admin_middleware' => ['web', 'auth', 'can:manage-saml'],
Or use a Gate in your AuthServiceProvider:
Gate::define('manage-saml', function ($user) { return $user->hasRole('admin'); });
Handle Authentication Events
Publish a standard listener:
php artisan saml2:publish-listener
This creates app/Listeners/HandleSaml2Login.php:
public function handle(Saml2LoginEvent $event): void { // Find or create user $user = User::firstOrCreate( ['email' => $event->getEmail()], ['name' => $event->getName()] ); // Log them in Auth::login($user); }
Note: In Laravel 11/12, events are auto-discovered.
Integrate with Your Routes
Simple - a login link:
<a href="{{ route('saml2.login', ['idp' => 'azure']) }}"> Login with Azure AD </a>
Full Integration - replacing auth routes:
// routes/auth.php Route::middleware('guest')->group(function () { if (app()->environment('local')) { // Local login for development Route::get('/login', [AuthController::class, 'showLogin'])->name('login'); Route::post('/login', [AuthController::class, 'authenticate']); } else { // SAML Login - redirects to IDP Route::get('/login', function () { return redirect()->route('saml2.login', ['idp' => 'your-idp-key']); })->name('login'); } }); Route::middleware('auth')->group(function () { if (app()->environment('local')) { Route::post('/logout', [AuthController::class, 'logout'])->name('logout'); } else { // SAML Logout (SLO) Route::match(['get', 'post'], '/logout', function () { return redirect()->route('saml2.logout'); })->name('logout'); } });
Artisan Commands
| Command | Description |
|---|---|
saml2:create-idp {key} |
Create a new IDP |
saml2:list-idps |
List all configured IDPs |
saml2:test-idp {key} |
Test IDP configuration |
saml2:delete-idp {key} |
Delete an IDP |
saml2:generate-cert |
Generate SP certificates |
saml2:refresh-metadata |
Refresh IDP metadata from URLs |
saml2:publish-listener |
Publish standard login listener |
saml2:reset-setup |
Reset to first-deploy state |
Reset Setup Command
If you need to reconfigure from scratch:
# Reset setup state only (keeps IDPs) php artisan saml2:reset-setup # Reset setup state AND delete all IDPs php artisan saml2:reset-setup --with-idps
After running this, the setup wizard at /saml2/setup will be accessible again.
Routes
The package registers these routes:
SAML Routes
| Route | Method | Description |
|---|---|---|
/saml2/setup |
GET | First-deploy setup wizard |
/saml2/login/{idp?} |
GET | Initiate SSO login |
/saml2/acs |
POST | Generic ACS - auto-detects IDP |
/saml2/acs/{idp} |
POST | ACS with explicit IDP key |
/saml2/sls/{idp} |
GET/POST | Single Logout Service |
/saml2/metadata |
GET | SP Metadata XML |
/saml2/logout/{idp?} |
GET | Initiate logout |
Admin Routes
| Route | Method | Description |
|---|---|---|
/saml2/admin |
GET | Admin dashboard |
/saml2/admin/idp/create |
GET | Create IDP form |
/saml2/admin/idp |
POST | Store new IDP |
/saml2/admin/idp/{id} |
GET/PUT | Edit/update IDP |
/saml2/admin/idp/{id} |
DELETE | Delete IDP |
/saml2/admin/idp/{id}/toggle |
POST | Activate/deactivate |
/saml2/admin/idp/{id}/mapping |
GET/POST | Attribute mapping editor |
/saml2/admin/idp/{id}/refresh |
POST | Refresh metadata |
Attribute Mapping
Attribute mapping allows you to normalize SAML attributes from different IDPs.
Global Mapping (Config)
Set default mapping in config/beartropy-saml2.php:
'attribute_mapping' => [ 'email' => 'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress', 'name' => 'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name', 'first_name' => 'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname', 'last_name' => 'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname', ],
Per-IDP Mapping (Admin Panel or Code)
Each IDP can have custom mapping that overrides the global config.
Via Admin Panel:
- Go to
/saml2/admin - Click "Mapping" on any IDP
- Toggle "Use global mapping" off
- Add your custom field mappings
Via Code:
use Beartropy\Saml2\Models\Saml2Idp; $idp = Saml2Idp::where('key', 'azure')->first(); $idp->attribute_mapping = [ 'email' => 'http://schemas.microsoft.com/identity/claims/emailaddress', 'name' => 'http://schemas.microsoft.com/identity/claims/displayname', 'roles' => 'http://schemas.microsoft.com/ws/2008/06/identity/claims/groups', ]; $idp->save();
Using Attributes in Your Listener
public function handle(Saml2LoginEvent $event): void { // Mapped attributes (uses IDP-specific or falls back to global) $email = $event->getEmail(); // Shorthand $name = $event->getName(); // Shorthand $custom = $event->getAttribute('roles'); // Custom mapped field // Raw SAML attributes (always available) $rawEmail = $event->getRawAttribute('http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress'); // All data $allMapped = $event->attributes; $allRaw = $event->rawAttributes; }
Configuration Reference
IDP Source
// config/beartropy-saml2.php // 'env' - Single IDP from environment only // 'database' - Multiple IDPs from database // 'both' - Check env first, then database 'idp_source' => env('SAML2_IDP_SOURCE', 'database'),
Environment-based IDP
For single IDP setups via .env:
SAML2_IDP_SOURCE=env SAML2_IDP_KEY=default SAML2_IDP_NAME="My IDP" SAML2_IDP_ENTITY_ID=https://idp.example.com SAML2_IDP_SSO_URL=https://idp.example.com/sso SAML2_IDP_SLO_URL=https://idp.example.com/slo SAML2_IDP_CERT="MIICpDCCAYwCCQ..."
Redirects
'login_redirect' => env('SAML2_LOGIN_REDIRECT', '/'), 'logout_redirect' => env('SAML2_LOGOUT_REDIRECT', '/'), 'error_redirect' => env('SAML2_ERROR_REDIRECT', '/login'),
Events
Saml2LoginEvent
Dispatched after successful SAML authentication.
Properties:
$event->idpKey // Key of the authenticating IDP $event->nameId // SAML NameID (usually email) $event->attributes // Mapped attributes (per config) $event->rawAttributes // Original SAML attributes $event->sessionIndex // For SLO
Helper Methods:
$event->getEmail(); // Get email $event->getName(); // Get name $event->getAttribute('field'); // Get mapped attribute $event->getAttribute('field', 'default'); // With default $event->getRawAttribute('saml_uri'); // Get raw SAML attribute $event->toArray(); // All data as array
Saml2LogoutEvent
Dispatched after successful SAML logout.
Security
For production environments, enable signature and encryption:
'security' => [ 'authnRequestsSigned' => true, 'wantAssertionsSigned' => true, 'signMetadata' => true, ],
Generate SP certificates first with php artisan saml2:generate-cert.
Internationalization
The package includes English and Spanish translations. To publish and customize:
php artisan vendor:publish --tag=beartropy-saml2-lang
This creates files in lang/vendor/beartropy-saml2/.
Customizing Views
To customize the setup wizard or admin panel views:
php artisan vendor:publish --tag=beartropy-saml2-views
This publishes views to resources/views/vendor/beartropy-saml2/.
Note: The UI is vanilla HTML/CSS (no Tailwind or Livewire dependencies), so it works with any Laravel stack.
Documentation
For more detailed information, please refer to the following guides:
Includes detailed guides for Configuration, Installation, Initial Setup, and the Admin UI.
License
MIT License - see LICENSE.md