Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Filament Passkeys Laravel Package

robertboes/filament-passkeys

Filament Passkeys adds passwordless WebAuthn passkey login to Filament panels. Users register and sign in with biometrics or hardware security keys, and manage their own passkeys. Simple setup, configurable, and fully integrated with Filament's authentication flow.

View on GitHub
Deep Wiki
Context7

Getting Started

Assuming the package wraps laravel/passkeys (WebAuthn) and exposes a Filament login button plus a credential-management UI:

composer require robertboes/filament-passkeys-plugin
php artisan vendor:publish --tag="filament-passkeys-migrations"   # check exact tag in README
php artisan migrate

Register the plugin on the panel(s) that should accept passkeys:

// app/Providers/Filament/AdminPanelProvider.php
public function panel(Panel $panel): Panel
{
    return $panel
        // ...
        ->plugin(PasskeysPlugin::make());
}

Then set the relying-party values — these are the ones that actually make or break the feature:

// config/filament-passkeys.php (names approximate — verify against src/)
'relying_party_id' => parse_url(config('app.url'), PHP_URL_HOST), // e.g. "example.com"
'origin'           => config('app.url'),                          // full scheme+host+port
'user_verification' => 'preferred',

First use case to validate end-to-end: log in as a user, go to the security/profile page the plugin registers, click Add passkey, complete the OS prompt (Touch ID / Windows Hello / security key), log out, and sign back in from the Filament login screen using the passkey button. If that loop works on a real device over HTTPS, the integration is sound.

Where to look first, in order:

  1. README.md — installation and the exact plugin/config class names.
  2. config/filament-passkeys.php — relying party, origin, user verification, and any toggle for auto-registration on the login page.
  3. src/ — the Filament page/plugin classes and the controllers handling the navigator.credentials.create/get round-trip. This is a small package; read it all before you ship it.
  4. database/migrations/ — the passkeys table shape (credential id, public key, counter, transports, last_used_at).

Implementation Patterns

Per-panel rollout. Don't enable it everywhere by default. Register the plugin only on the panel where it's earned its keep, and keep the password form on every panel as a fallback:

->plugin(PasskeysPlugin::make())
->login()          // keep the password form

Treat passkeys as an additive second factor, then promote. The safest sequence for an existing app is: users enroll a passkey while still logging in with a password, run it for a release cycle, and only then consider passwordless-first. Because the package sits on top of Filament's auth guard, you can gate enrollment behind a policy or a simple check in a custom page:

public static function canAccess(): bool
{
    return auth()->user()?->hasRole('admin')
        && auth()->user()->passkeys()->count() < 5;
}

Require recent password confirmation before enrolling. A passkey added on a hijacked session is a permanent backdoor. Wrap the enrollment action in Filament's password confirmation (or your own password.confirm middleware) so the user re-enters their password immediately before the WebAuthn prompt.

Give users a management surface. Even if the plugin ships a page, most teams end up with a small Filament page or relation manager listing credentials by name, created_at, last_used_at, with a Revoke action. Nudge users to name devices ("MacBook – Chrome") at registration; an unnamed list of four credentials is unusable for support.

Always keep a recovery path. Never let a user delete their last credential if password login is disabled for them, and never disable password login for an account with zero passkeys. For admins, provide an artisan command or Filament action to purge a user's passkeys — otherwise "I lost my phone" becomes a manual DB edit at 2am.

Pair with email-based recovery. For passwordless flows, pair passkeys with a signed magic link or Fortify's password reset. The passkey is the fast path, not the only path.

Testing. You cannot exercise real WebAuthn in a plain PHPUnit feature test — there's no authenticator. Split it:

  • Unit/feature test the challenge issuance, persistence and the save endpoint by posting a crafted/serialized credential fixture (capture one from a dev browser and replay it against a test key pair).
  • Use Pest browser / Dusk with Chrome's virtual authenticator (CDP WebAuthn.enable + WebAuthn.addVirtualAuthenticator) for the actual create/get ceremony.
  • Assert the negative cases: replayed challenge, mismatched origin, wrong RP ID, revoked credential.

Gotchas and Tips

HTTPS is non-negotiable. navigator.credentials only exists in a secure context. localhost is exempt, http://staging.example.com is not. If the button renders but nothing happens, check for a secure-context error in the console before blaming the package.

RP ID and origin must match reality, not APP_URL. The browser validates the origin against the RP ID. A mismatch between your proxy/load-balancer host, APP_URL, and config('filament-passkeys.origin') produces confusing SecurityErrors. Behind a proxy, make sure TrustProxies is configured so Laravel sees the real scheme and host. Subdomains matter: rp_id = example.com covers admin.example.com; rp_id = admin.example.com does not cover the apex.

Credentials are bound to the origin. Everything enrolled on staging is useless in production and vice versa. Plan for re-enrollment between environments, and consider disabling the plugin entirely outside production to avoid confusing testers.

Synced vs device-bound passkeys. iCloud Keychain and Google Password Manager sync credentials; hardware keys don't. Users will assume a passkey "is on their phone" and be surprised on a new device. Set user verification to preferred rather than required unless you have a compliance reason — required breaks some security keys and older platform authenticators.

The assertion endpoint is a POST from JS, not a Livewire call. If you see 419s, it's CSRF/session — verify the endpoint is inside the right middleware group (web) and that the fetch includes credentials. Filament panel middleware stacks differ per panel; a route that works on admin may 419 on a second panel.

This package is very young — 3 stars, 0 dependents, single release. That is the dominant risk here, not the WebAuthn code. Mitigations:

  • Pin the exact version in composer.json; don't use ^ on a 0.x/1.x with one release.
  • Read the whole src/ tree (~an afternoon) so you can debug and fork it. Budget for owning it.
  • Check the underlying web-auth/webauthn-lib / laravel/passkeys version it requires and whether that is maintained; the real security surface is there.
  • Prefer laravel/passkeys + Fortify directly if your auth flow is simple and you only need a login button — you keep the dependency count down and Filament gives you the views anyway.
  • Watch the release date (a future-dated release is a packaging/tagging smell — confirm the tag actually resolves and the changelog is real).

Extension points worth knowing. Publish the views and translations before you edit anything in vendor/; use a custom Passkey model via config if you need extra columns (device label, IP, last-used tracking); listen for whatever registration/authentication events the package dispatches to feed an audit log. If the package doesn't expose an event you need, that's a signal to wrap its controllers in your own rather than patch it.

Debugging checklist, in order: console error in the browser → chrome://webauthn-internals (or Safari's WebAuthn pane) → check the RP ID/origin the browser reports vs your config → confirm the challenge row was persisted and consumed → check storage/logs for the underlying WebAuthn library's exception, which is far more specific than anything Filament shows you.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
htinlynn/genius-helper
maco-studios/hephaestus
elmogy/larafast
xeviant/laravel-paystack
gorilladash/laravel-website-sdk
martialbe/flysystem-oss
chondal/3312client
jeroen-g/flickr
opscale-co/nova-genbi
itsjustvita/laravel-retention
tequilarapido/trackit
gregsphotography/site-builder-filament
cabaoctavio/laravel-query-log
gregsphotography/site-builder-components
io-digital/clickatell
snowsoft/laravel-admin
oxygen/auth
relicsoftware/barakah-payment-gateway
tobya/work-with-composer
equipc/laravel-email-validator