[B b65.ch](/)

![](https://www.b65.ch/images/pocketid-filament-oidc-hero.jpg)

On this page

15.05.2026

# Self-Hosted OIDC for Your Filament Admin with PocketID

* * *

![](https://www.b65.ch/images/pocketid-filament.png)

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](https://github.com/stonith404/pocket-id) 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:

- [brufdev/socialiteproviders-pocketid](https://github.com/brufdev/socialiteproviders-pocketid): a Socialite driver for PocketID's OIDC endpoints
- [dutchcodingcompany/filament-socialite](https://github.com/dutchcodingcompany/filament-socialite): wires Socialite providers into Filament's login flow

`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.

© 2026 Simon Jenny [Blog (Englisch)](/blog/index.html) [Kontakt](/contact.html)

- [**{{title}}**  
  {{excerpt}}](%7B%7Burl%7D%7D)