nurbekjummayev/filament-tdc-sso
Integrate TDC single sign-on with Filament admin panels using this Laravel package. It provides SSO authentication, configurable login flows, and streamlined user access management, helping developers secure Filament applications with less custom integration work and fewer login friction points.
composer require nurbekjummayev/filament-tdc-sso
php artisan vendor:publish --tag="filament-tdc-sso-config"
Register the plugin on the panel(s) that should use SSO:
public function panel(Panel $panel): Panel
{
return $panel
->plugin(TdcSsoPlugin::make());
}
Set the credentials in .env (names come from the published config/filament-tdc-sso.php — client id, client secret, base/authorize/token URLs, redirect URI, scopes), then php artisan config:clear.
Where to look first: the published config file (every knob in one place), then the plugin class itself — that's where the render hook for the login button and the callback route registration live. Check php artisan route:list | grep -i sso to confirm the callback route name and URI before you hand the redirect URI to whoever administers the IdP.
First use case: keep the normal Filament email/password login as a break-glass path for a handful of admins, and add "Login with TDC" for everyone else. Match returning users by email; for first-time users decide up front whether to auto-provision or reject unknown emails (most teams start by rejecting, then flip provisioning on once the role assignment story is settled).
User resolution is the whole integration. Everything else is plumbing. Wrap the package's resolver callback in your own service so both matching and provisioning are testable in isolation:
// config/filament-tdc-sso.php (or a service provider)
'resolve_user' => [App\Support\SsoUserResolver::class, 'resolve'],
public function resolve(array $claims): ?Authenticatable
{
$email = Str::lower($claims['email'] ?? '');
$user = User::whereRaw('lower(email) = ?', [$email])->first();
if (! $user && config('filament-tdc-sso.provision')) {
$user = User::create([
'name' => $claims['name'] ?? $email,
'email' => $email,
'password' => Hash::make(Str::random(64)), // never left usable
]);
$user->assignRole('panel-user'); // spatie/permission — set this explicitly
}
return $user?->is_active ? $user : null;
}
Returning null for disabled users is the cleanest way to block access; don't rely on the IdP to do it for you.
Multi-panel / multi-guard. Register the plugin per panel with the correct guard and a distinct redirect URI per panel (e.g. /admin/auth/sso/callback vs /app/auth/sso/callback). Reusing one redirect URI across panels works, but you then have to remember which panel started the flow — store the intended panel id in the session before redirecting and consume it in the callback.
Post-login redirect. Override the default landing URL rather than fighting it: point users at a dashboard route, and in multi-tenant Filament setups resolve and Filament::setTenant() (or redirect to the tenant-scoped URL) after authentication, since the tenant isn't known until you have the user.
Local development and CI. Never call the real IdP from tests. Http::fake() the token and userinfo endpoints, then hit the callback route as a feature test:
Http::fake([
'*/token' => Http::response(['access_token' => 'test', 'token_type' => 'Bearer']),
'*/userinfo' => Http::response(['email' => '[email protected]', 'name' => 'Dev']),
]);
$this->get(route('filament-sso.callback', ['state' => session('sso_state')]))
->assertRedirect('/admin');
$this->assertAuthenticatedAs($user);
For manual local work, bind a fake client implementation in a local-only service provider instead of pointing at production credentials.
Audit and lifecycle. Log successful and failed SSO logins (user id, email from claims, IdP subject id, IP) — this is the first thing you'll want when someone reports "I can't get in." Persist the IdP subject (sub) on the user record: matching on email alone breaks the day someone changes their surname at the IdP.
"nurbekjummayev/filament-tdc-sso": "0.1.*") and treat every minor bump as potentially breaking. Read the commit list at the changelog URL before upgrading; assume config keys and the resolver signature may move.state / InvalidState errors are almost always session, not SSO. SESSION_DOMAIN, APP_URL, and the callback host must agree, and SameSite must permit the cross-site redirect back from the IdP. Test in a private window — a stale session cookie masks the real cause.TrustProxies and force HTTPS, otherwise the generated redirect_uri is http:// and the IdP rejects the exchange with an opaque error.config:cache traps secrets. Changing TDC_SSO_CLIENT_SECRET in .env does nothing until you re-run php artisan config:cache in production.admin and then test /app), and that you haven't disabled the default login page with ->login() variations.client_secret) on non-2xx from the token endpoint — IdPs return the actual reason there and nowhere else. Telescope on the callback route pays for itself immediately.config/filament-tdc-sso.php is the source of truth — rename anything above to match what actually ships.How can I help you explore Laravel packages today?