← LIST / 2026-05-15 / Laravel
POST · LARAVEL

Self-Hosted OIDC for Your Filament Admin with PocketID

2026-05-15 Published 3 min read 597 words #laravel #filament #oidc #self-hosting
Self-Hosted OIDC for Your Filament Admin with PocketID

PocketID

Out of the box, Filament ships with a classic email/password login form. For a personal admin panel or a small team tool that's often fine — but it means storing password hashes in your database and building a full credential-management flow. And if you reach for Google or GitHub OAuth instead, you're trading one dependency for another.

I wanted something different: a single sign-on provider I control, running on my own infrastructure. That's where PocketID comes in.

What is PocketID?

PocketID is a lightweight, self-hosted OIDC provider. A single Docker container, a domain, and a client registration is all it takes. No user database gymnastics — it handles authentication and hands back a standard OIDC token. Your Laravel app trusts that token and creates (or finds) a local user record. Done.

Packages

Two packages do the heavy lifting:

composer require brufdev/socialiteproviders-pocketid dutchcodingcompany/filament-socialite

Configuration

config/services.php — add PocketID credentials:

'pocketid' => [
    'client_id'     => env('POCKETID_CLIENT_ID'),
    'client_secret' => env('POCKETID_CLIENT_SECRET'),
    'redirect'      => env('POCKETID_REDIRECT_URI'),
    'base_url'      => env('POCKETID_URI'),
],

app/Providers/AppServiceProvider.php — register the Socialite driver via an event listener:

use Illuminate\Support\Facades\Event;

Event::listen(function (\SocialiteProviders\Manager\SocialiteWasCalled $event) {
    $event->extendSocialite('pocketid', \brufdev\SocialiteProvidersPocketID\Provider::class);
});

app/Providers/DashboardPanelProvider.php — add the plugin to your panel:

use DutchCodingCompany\FilamentSocialite\FilamentSocialitePlugin;
use DutchCodingCompany\FilamentSocialite\Models\Contracts\FilamentSocialiteUser as FilamentSocialiteUserContract;
use DutchCodingCompany\FilamentSocialite\Provider;

FilamentSocialitePlugin::make()
    ->providers([
        Provider::make('pocketid')
            ->label(__('Sign in'))
            ->outlined(false)
            ->stateless(false),
    ])
    ->redirectAfterLoginUsing(function (string $provider, FilamentSocialiteUserContract $socialiteUser, FilamentSocialitePlugin $plugin) {
        return redirect('/dashboard');
    })
    ->showDivider(false)
    ->registration(true),

registration(true) means the first OAuth login auto-creates a local user — no manual user provisioning needed.

Skipping the Login Form Entirely

With standard Filament Socialite, the login page shows a form with an OAuth button below. For a panel that only supports PocketID, that form is noise. Override the login page to redirect immediately:

app/Filament/Auth/Login.php:

namespace App\Filament\Auth;

use Filament\Auth\Pages\Login as BaseAuth;
use Filament\Schemas\Components\Component;
use Filament\Schemas\Components\EmbeddedSchema;
use Filament\Schemas\Components\Form;

class Login extends BaseAuth
{
    public function mount(): void
    {
        redirect(config('app.url') . '/oauth/pocketid');
    }

    public function getFormContentComponent(): Component
    {
        return Form::make([EmbeddedSchema::make('form')])
            ->id('form')
            ->visible(fn (): bool => false);
    }

    protected function getFormActions(): array
    {
        return [];
    }
}

Register it in your panel provider:

->login(Login::class)

Now anyone hitting /dashboard unauthenticated is sent straight to PocketID, without ever seeing a username field.

Environment Variables

POCKETID_CLIENT_ID=your-client-id
POCKETID_CLIENT_SECRET=your-client-secret
POCKETID_REDIRECT_URI=https://your-app.com/oauth/callback/pocketid
POCKETID_URI=https://auth.your-domain.com

How It Works

  1. User navigates to /dashboard — Filament detects unauthenticated, renders the Login page
  2. Login::mount() fires immediately, redirecting to APP_URL/oauth/pocketid
  3. filament-socialite adds a state parameter (encrypted panel ID) and hands off to PocketID
  4. User authenticates on PocketID's own UI
  5. PocketID redirects back to /oauth/callback/pocketid with an authorization code
  6. filament-socialite exchanges the code for a token, fetches the user from PocketID's /api/oidc/userinfo
  7. A SocialiteUser record is created (or found) and linked to a local User — no password stored
  8. User lands on /dashboard

The result: zero password hashes in your database, no reliance on external OAuth providers, and a login UX that's a single redirect.

END OF POST READY.