postiefs/laravel-sso
postiefs/laravel-sso is a Laravel package for adding single sign-on to your app. It provides the building blocks to connect an SSO provider, streamline authentication, and keep login flows consistent across Laravel projects.
composer require postiefs/laravel-ssophp artisan sso:install — publishes the config and scaffolds the pieces the package needs (routes, guard/middleware wiring, .env keys for the portal URL, client id/audience and HS256 signing secret).php artisan sso:check — run this immediately after install. It is the fastest way to confirm the portal is reachable, the issuer/audience/secret are correct, and that your Callback URL matches what the portal has registered.docs/ folder. Two knobs matter day-to-day: the portal connection settings and check_interval (how often /auth/check-access is called).First use case: protecting an existing app area with portal identity instead of local passwords.
Route::middleware('sso.auth')->group(function () {
Route::get('/dashboard', DashboardController::class);
});
Point users at the package's login route, let callback exchange the launch token, and let the shipped UserResolver find-or-create a user by email. From there, replace any Auth::attempt() in your login controller with a redirect to the SSO login route — the local password path should go away entirely, not live alongside it.
Route protection is three middlewares, not one. Treat them as a stack with distinct meanings and verify the exact semantics in the docs before mixing them:
| Middleware | Role |
|---|---|
sso |
Entry points / launch handling — the unauthenticated side of the flow |
sso.auth |
Requires a valid signed-in SSO session; use on protected app routes |
sso.session |
Enforces the portal-side session check (the /auth/check-access poll) |
Put your own authorization middleware after sso.auth, and keep package middleware ahead of anything that touches Auth::user().
Customise identity with UserResolver. The default finds-or-creates by email and never mutates an existing user. The moment you need a different lookup key (employee number, tenant-scoped email) or extra provisioning (default team, locale, profile sync), bind your own implementation in a service provider. This is the single most important extension point — most real apps will override it.
Do the "never changes an existing user" work yourself. Because the default resolver leaves existing records alone, profile drift (name changes, department moves) will not self-heal. Hook SsoUserCreated for first-time provisioning and handle updates explicitly, either in your resolver or from SsoLoggedIn:
Event::listen(SsoUserCreated::class, function ($event) {
$event->user->assignRole('member'); // spatie/laravel-permission, etc.
});
Event::listen(SsoLoggedIn::class, function ($event) {
$event->user->update(['name' => $event->claims['name'] ?? $event->user->name]);
});
Roles staying in your app is deliberate — the package adds no columns and no migrations, so your permission model is untouched. Seed default roles on SsoUserCreated rather than in a migration.
Handle session end explicitly. SsoSessionEnded fires when the portal ends the session; pair it with the ended route so your UI lands somewhere sensible instead of a bare 401. Use it for audit logging and cache/impersonation invalidation.
Use @ssoSessionGuard in your layout, once. Drop it into the authenticated layout (not per-page) so the deadline timer and heartbeat are present on every screen. Without it, the browser will not proactively sign out at the sexp deadline or send heartbeats — the server-side check still applies, but the UX degrades to surprise redirects.
Reach for the Sso facade for read-only session facts (current deadline, remaining time) when rendering a "signed in until…" indicator or gating a long-running action.
Testing: LaunchToken lets you mint tokens in feature tests without a browser round-trip to the portal. Combine it with actingAs() for everything downstream of the callback, and stub/short-circuit the portal check so tests never hit the network. Assert on the events rather than on package internals.
sexp is adopted as-is. A user editing a long form or uploading a large file will be signed out mid-flight. Design for it: warn near the deadline, save drafts, and make the post-expiry redirect preserve the intended destination.check_interval is your revocation latency budget. Portal downtime deliberately fails open (users stay signed in). That is the right trade-off, but it means a revoked user survives until the next successful check — plus up to check_interval of slack. Keep it short enough to matter to security, long enough not to hammer the portal from every open tab./auth/check-access responses; otherwise a broken portal integration looks like normal operation.sso:check is your post-deploy smoke test.LaunchToken locally instead of trying to make the redirect work on localhost.php artisan config:cache re-run in cached environments, especially check_interval — it is easy to change in .env and see no effect.How can I help you explore Laravel packages today?