megaads/sso-client
megaads/sso-client is a Laravel/PHP package for connecting your application to a centralized single sign-on (SSO) provider. It helps streamline SSO authentication and user integration so services can share secure login.
This is a small, single-maintainer package (last release August 2021, no stars, no dependents), so treat the source as the documentation. Budget 30–60 minutes to read it before wiring it in.
Install and inspect the constraint first.
composer require megaads/sso-client
Immediately open vendor/megaads/sso-client/composer.json and check the illuminate/* and php constraints. If you are on Laravel 9+ you will likely need --ignore-platform-reqs or a fork with widened constraints.
Register the provider. Check src/*ServiceProvider.php for whether it is auto-discovered and whether it registers a facade or a guard driver. Add it to config/app.php manually if discovery fails.
Publish and read the config.
php artisan vendor:publish --provider="Megaads\SsoClient\SsoServiceProvider"
Open config/sso.php and map every key to an env var. The keys you care about are the SSO server base URL, the client/app identifier, the client secret, and the callback/redirect URI.
Where to look first, in order:
config/sso.php — the contract with the SSO server.src/Http/Middleware/ — how it decides a user is authenticated.src/Http/Controllers/ — the login redirect and callback handler.routes/*.php — which URIs the package registers (login, callback, logout).src/Models/ or a trait — how it materialises a local user.First use case: protect a single dashboard route group, redirect unauthenticated users to the SSO server, and on callback log them into Laravel's session. Do not migrate your whole auth stack on day one — put the middleware on one route group and keep the default web guard working alongside it.
Middleware-guarded route group. The package's value is a middleware that bounces guests to the SSO login endpoint. Apply it to a group rather than globally:
Route::middleware(['web', 'sso.auth'])->group(function () {
Route::get('/dashboard', DashboardController::class);
});
Confirm the exact alias in the service provider ($router->aliasMiddleware(...)).
Callback → local user mapping. The callback controller will hand you an identity payload (array or DTO). The day-to-day pattern is to resolve it to an Eloquent user with a stable key, never by email alone:
$user = User::firstOrCreate(
['sso_id' => $payload['id']],
['name' => $payload['name'], 'email' => $payload['email']]
);
Auth::login($user, true);
Add a sso_id column with a unique index. On every login, sync roles/attributes from the SSO payload so deprovisioning propagates.
Logout that actually logs out. Local Auth::logout() leaves the SSO session alive, so the next request silently re-authenticates. Use the package's logout route (or redirect to the SSO server's logout URL) and invalidate the session + regenerate the CSRF token.
Testing. The callback makes an outbound HTTP call, so stub it:
Http::fake(['sso.example.com/*' => Http::response(['id' => 1, 'email' => '[email protected]'])]);
$this->get('/sso/callback?code=test')->assertRedirect('/dashboard');
Assert both the happy path and a tampered/expired token path.
Integration tips.
web for session access.VerifyCsrfToken::$except); prefer keeping it on if state is validated server-side.composer.json, and plan to fork into packages/ if you need to bump Laravel support.iss/aud/exp, or a plain trusted GET. Anything that trusts an identifier passed in the query string is a critical vulnerability. Do not ship to production on faith.http vs https mismatch against what is registered on the SSO server is the #1 cause of "invalid_client"/"invalid_redirect_uri" errors. Check the value after php artisan config:cache.php artisan config:clear. During local debugging, run config:clear after every .env edit.domain, SameSite, and Secure settings all matter. Session state mismatches usually mean the state cookie was dropped — set SESSION_DOMAIN deliberately and test in a real browser, not just php artisan serve with curl.defer, register() signatures, or facade aliases that newer versions tolerate but deprecate. Watch storage/logs/laravel.log for BindingResolutionException on the guard.Log::debug() in the callback controller rather than guessing.vendor/.How can I help you explore Laravel packages today?