Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Laravel Saml2 Laravel Package

24slides/laravel-saml2

Integrate SAML2 single sign-on into Laravel applications. This package wraps the OneLogin SAML PHP Toolkit, providing service provider routes, IdP configuration, metadata endpoints, login/logout handling, and customizable user resolution for enterprise SSO.

View on GitHub
Deep Wiki
Context7

Getting Started

Since 2.7.2 the package supports Laravel 13 (alongside the existing Laravel 10/11/12 range), and dependency constraints were bumped — pin scaler-tech/laravel-saml2: ^2.7.2 if you are on Laravel 13.

composer require scaler-tech/laravel-saml2
php artisan vendor:publish --tag=saml2-config   # publishes config/saml2.php

First places to look:

  1. config/saml2.php — this is the whole package. It holds three blocks you must fill: sp (your app: entityId, ACS/SLS URLs, x509cert + privateKey), idp (the customer's IdP metadata: entityId, SSO/SLO endpoints, signing cert), and security (signing/encryption requirements).
  2. Your ACS route — the Assertion Consumer Service endpoint. This is where the IdP POSTs back and where you hook your app logic.
  3. The package's event — login/logout are dispatched as events you listen to; that is the seam between SAML and your User model.

First use case — SP-initiated login:

// routes/web.php
Route::get('/login/saml', fn () => Saml2::login(route('saml.acs')))->name('saml.login');

Point the IdP at your metadata endpoint (/saml2/metadata by default) so it can pull your entityId and ACS URL rather than you hand-typing them. Hand-typing is the #1 source of "Invalid audience" errors.

Get one IdP working end-to-end against a test tenant before wiring the user model. The package will happily authenticate a session without ever creating a User — that mapping is your job (see below).

Implementation Patterns

Event-driven user mapping. Do not put user creation in a controller. Register a listener and keep the SAML package ignorant of your domain:

Event::listen(Saml2LoginEvent::class, function (Saml2LoginEvent $event) {
    $user = $event->getSaml2User();

    $user = User::updateOrCreate(
        ['email' => $user->getAttribute('mail')[0]],
        ['name' => $user->getAttribute('displayName')[0] ?? ''],
    );

    Auth::login($user);
    // optionally: sync roles from groups, record idp session index for SLO
});

The getAttribute() values are always arrays per the SAML spec — always index [0] and always null-guard.

Two routes, two flows. Keep SP-initiated (GET /login/saml → IdP → POST /saml2/acs) and IdP-initiated (IdP dashboard → POST /saml2/acs) on the same ACS endpoint. The package distinguishes them; your listener should not assume a RelayState or a stored AuthnRequest id exists.

Middleware guard. Wrap authenticated route groups with the package's SAML middleware rather than hand-rolling Auth::check() redirects, then layer your own authorization on top. Keep the SAML middleware outside auth so the redirect to the IdP happens before Laravel's Authenticate middleware bounces to a login page that doesn't exist.

Single Logout. Configure sls in both sp and idp and store the IdP SessionIndex at login — you need it to build a valid LogoutRequest. SP-initiated logout works out of the box; IdP-initiated SLO (IdP calls your SLS) needs the session index to invalidate the right local session.

Multi-tenant / per-tenant IdP. Don't fight the singleton config. Keep the default config for the "primary" tenant and instantiate the client with an overridden config array for others:

$saml = app(Saml2Auth::class);           // or new Saml2Auth(...)
$saml->loadSettings(array_merge(config('saml2'), $tenantOverrides));
return $saml->login($tenantAcsUrl);

Resolve the tenant from the hostname or a route parameter in a small service class, and cache the built settings array.

Testing. You can't round-trip a real IdP in CI. Two options that work: (a) Event::fake() the login event and assert your listener in isolation, or (b) keep a captured, non-expired SAMLResponse fixture and POST it to the ACS route with a frozen clock. Option (a) for most tests, (b) for one integration test that proves the ACS route and config are wired.

Gotchas and Tips

route:cache will bite you. The package registers its routes from config; if useRoutes is on and you later change the ACS path, a stale route cache (or an old .env snapshot after config:cache) will point the IdP at a 404 — and the IdP will usually report a generic "authentication failed". Always php artisan optimize:clear when touching SAML config.

The IdP POSTs from an external origin. Laravel's session.same_site default will silently drop your session on the ACS callback in some browsers. Set SESSION_SAME_SITE=none + SESSION_SECURE_COOKIE=true (HTTPS required), or you'll get an infinite login loop that looks like a credential problem.

Behind a load balancer, your ACS URL will be http://. Configure TrustProxies correctly or the generated ACS/metadata URLs will mismatch the IdP registration and every assertion will fail audience validation.

wantAssertionsSigned vs wantMessagesSigned are not interchangeable. Most IdPs sign assertions only. Turning on message signing when the IdP doesn't sign the response produces "Invalid signature" with no hint about which of the two settings is at fault. Start with both off, confirm the flow, then tighten.

Certificate formatting. Paste the IdP cert exactly as given, including BEGIN/END lines, with real newlines. Trailing whitespace, CRLF from a Windows export, or a cert pasted from a PDF are common silent failures. If your cert lives in an env var, \n escaping is a frequent culprit — use a heredoc in the env or store it in a file.

Certificate rotation is the top cause of "it worked yesterday" breakage. IdP signing certs roll on a schedule (often a yearly one), and they usually roll without notifying you. Log the cert fingerprint/subject at startup and alert on mismatch rather than discovering it at login time.

openssl extension is mandatory. SP signing and decryption both depend on it; a Docker image without ext-openssl will fail with an unhelpful Saml2Exception at first use, not at install.

Clock skew. Assertions carry NotBefore/NotOnOrAfter with tight windows. Containers with drifting clocks fail intermittently. Make sure chrony/systemd-timesyncd runs on your app hosts; the security block exposes a tolerance but raising it is a symptom fix, not a cure.

Debugging. Set the package's debug/test-mode flag and route its logger to a dedicated channel — the useful detail ("audience mismatch", "recipient mismatch", "response not signed") is in the library's internal log, but it's drowned out by your application noise if it shares the default channel. Never leave debug mode on in production: the library will dump the raw assertion, which includes group memberships and email addresses.

Extending. The two clean seams are the events (login/logout) and the config array. Reach for subclassing Saml2Auth only if you must change the AuthnRequest itself (e.g. ForceAuthn, NameIDPolicy format). Avoid patching the vendor tree — a patch applied to onelogin/php-saml settings will be lost on the next composer update, and 2.7.2 just showed how readily dependency versions move.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
robertmarney/lara-hierarchial-collections
simonschaufi/rule-doc-generator
emissaoja/emissaoja
blackbricksoftware/gravityforms-civicrm-form-processor
al-rahabi/fancy-crud
jinas/dhivatar-component
mltstephane/laravel-quality
bernskiold/laravel-talentlms
bernskioldmedia/laravel-form-architect
youbar/easy-crud
c-point-labs/laravel-hostyourai
dbravoan/dbcms-admin-theme-dbcms
dbravoan/dbcms-theme-db-cyber
dbravoan/dbcms-blocks-basic
dbravoan/dbcms-module-content-audit
dbravoan/dbcms-module-ai-assistant
paygate-to/paygate-php
padosoft/askmydocs-connector-base
defstudio/supercharged-carbon
bernskioldmedia/laravel-matomo