jeffersongoncalves/laravel-saml2
Laravel SAML2 package for adding SAML 2.0 single sign-on to your app. Configure an identity provider, handle SP-initiated login and logout, and map SAML assertions to Laravel users for enterprise SSO integration.
This is a multi-tenant SAML2 service-provider package: it holds per-tenant IdP configuration in a saml2_tenants table and wires that into the SAML login/logout/metadata flows.
Install (v1.1.0 flow):
composer require <vendor>/laravel-saml2
php artisan vendor:publish --tag=saml2-config
php artisan vendor:publish --tag=saml2-migrations
php artisan migrate
Note the migration is now a published stub, not something the package runs. If you skip the vendor:publish step you get a config file and no saml2_tenants table, and the failure surfaces later as a query error on the first login attempt rather than at boot.
Where to look first:
config/saml2.php — routes, guards, tenant resolution, debug flags.entityId, ssoUrl, x509cert, slsUrl, etc.) and is the source of truth for your tenant model/factory.First use case: get one tenant logging in end-to-end against a test IdP (samltest.id is the fastest loop) by seeding a single saml2_tenants row, hitting the login route, and confirming the ACS callback establishes an authenticated session. Only after that works should you add tenant resolution — debugging multi-tenancy and SAML signature validation at the same time is miserable.
Tenant resolution belongs in your app, not the package. Resolve the tenant in middleware (subdomain, path prefix, or a header) and bind it into the container so the SAML flow reads it consistently. Keep the resolution logic in one class — it's the piece you'll unit test most.
Seed tenants, don't hand-edit rows. A Saml2TenantFactory plus a seeder makes RefreshDatabase tests trivially reproducible. Because the migration is now an app-owned stub, tests running migrations in-memory work without any package-side migration registration hacks.
Route layout: if you're multi-tenant, prefix the package routes per tenant (e.g. /{tenant}/saml/acs) so the ACS URL embedded in the AuthnRequest matches the tenant. Register the SP metadata URL per tenant with each IdP — most IdPs cache metadata aggressively, so a URL change means re-registration.
Attribute mapping: treat the assertion as untrusted input. Map NameID/attributes to a local user in a single dedicated action, then Auth::login($user). Keep the mapping table in config or a per-tenant column so onboarding a new IdP doesn't require a deploy.
Deploy script:
php artisan vendor:publish --tag=saml2-migrations --force
php artisan migrate --force
--force matters because vendor:publish silently no-ops on an existing file — without it, an upgrade that changes the stub leaves your old copy in place and you'll never see the change.
Testing: don't try to forge signed assertions. Unit-test tenant resolution and attribute mapping against fixture payloads, and do one real end-to-end against a sandbox IdP per release. That's the only layer where signature/canonicalization bugs actually show up.
run_migrations key no longer exists. Leaving it in config/saml2.php is harmless today but will confuse the next reader, and if the package later validates config keys strictly it becomes a boot failure. Delete it.saml2_tenants migration, publishing plus migrate will not reconcile the two — diff them.--force) and review the diff. Add a note to your upgrade checklist.config:cache versus per-tenant config. If you swap SAML config at runtime per tenant, a cached config repository can bite you. Set values on the repository for the request and never assume they persist; anything stored statically leaks across tenants in long-running workers (Octane, queue workers).Invalid audience/Recipient mismatch rather than anything obviously proxy-related.\n in the DB is the single most common "invalid signature" cause.InvalidSignature (cert/formatting/canonicalization) from Response validation failed (audience, expiry, clock skew, replay). Check server clock skew before touching crypto.How can I help you explore Laravel packages today?