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

adriaanzon/filament-passkeys

Add passkey/WebAuthn authentication to Filament v5 panels. Use passkeys as a passwordless sign-in option with browser autofill, a second factor, or both. Users register and manage fingerprints, face ID, device PINs, and security keys from their profile page. Built on laravel/passkeys.

View on GitHub
Deep Wiki
Context7

Getting Started

Prerequisites to verify before you type a command. This package only targets Filament v5 and requires laravel/passkeys ^0.4. Check composer.json for filament/filament: ^5.0 first — on Filament v3/v4 this is a non-starter, and there is no backport branch. You also need a standard Authenticatable User on the panel's guard, ->login() and ->profile() enabled, and a non-SSO login page. If the login page is custom-rendered or the panel sits behind an SSO broker, stop and re-evaluate: the plugin hooks Filament's native login and MFA surfaces, so overrides break it silently rather than loudly.

The four installation steps, in order.

composer require adriaanzon/filament-passkeys
php artisan vendor:publish --tag="passkeys-migrations"
php artisan migrate
php artisan vendor:publish --tag="passkeys-config"   # optional but do it early

Then wire the contract and trait onto the model — this is app-wide, not panel-scoped:

use Filament\Models\Contracts\FilamentUser;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Laravel\Passkeys\Contracts\PasskeyUser;
use Laravel\Passkeys\PasskeyAuthenticatable;

class User extends Authenticatable implements FilamentUser, PasskeyUser
{
    use PasskeyAuthenticatable;
}

Pick your mode deliberately on day one. The three modes are not cosmetic — they change the user journey:

// 1. Passwordless sign-in only (no MFA challenge, profile UI still present)
->plugins([FilamentPasskeysPlugin::make()->passwordlessLogin()])
->multiFactorAuthentication([PasskeyAuthentication::make()->managementOnly()])

// 2. Passkey as second factor only (no passwordless button)
->plugins([FilamentPasskeysPlugin::make()])
->multiFactorAuthentication([PasskeyAuthentication::make()])

// 3. Both
->plugins([FilamentPasskeysPlugin::make()->passwordlessLogin()])
->multiFactorAuthentication([PasskeyAuthentication::make()])

First use case to test end-to-end. Start with mode 1 on a local panel: log in with password, go to Profile, register a passkey (Touch ID / Windows Hello), log out, then hit the "Sign in with passkey" button. That single loop exercises registration, credential storage, challenge, assertion, and session creation. Add the managementOnly() MFA entry at the same time so you confirm the profile UI renders in mode 1 without forcing a challenge.

Where to look first when something is wrong. Not this repo. Look at vendor/laravel/passkeys/config/passkeys.php (relying_party_id, allowed_origins, user_handle_secret, throttle, timeout) — 90% of "the browser prompt never appears" failures are RP-ID or origin mismatches there. Then look at config/fortify.php if Fortify is installed (see Gotchas).

Implementation Patterns

Staged rollout via config, not code. The mode levers map cleanly onto a phased rollout, and this is the main reason to use the plugin instead of a bespoke WebAuthn build. Practical sequence for an existing panel:

  1. Ship PasskeyAuthentication::make()->managementOnly() + ->passwordlessLogin() — nobody is forced into anything, but volunteers can register and use passkeys.
  2. After adoption plateaus, flip to mode 3 (PasskeyAuthentication::make() without managementOnly()), so volunteers get passkeys as a real second factor on password login.
  3. Enforce for privileged cohorts only (see below).

Enforce for super-admins while everyone else stays on password + TOTP. Filament's ->multiFactorAuthentication() takes an array, and providers are evaluated in order. Combine passkeys with a fallback so non-privileged users are never stranded:

use Filament\Auth\MultiFactor\Email\EmailAuthentication;

->multiFactorAuthentication([
    PasskeyAuthentication::make(),
    EmailAuthentication::make(),
])

For scoped enforcement, keep isRequired: true off the global call and instead drive an enforced flag per-user (a requires_passkey_mfa column checked in a Panel::canAccessPanel-adjacent gate or a custom MFA isRequired closure where Filament exposes one). Do not reach for isRequired: true + managementOnly() — that combination is explicitly broken.

Always pair passkeys with a fallback second factor in production. The README is direct about this: without a fallback, a lost or wiped device is a hard lockout with no self-service recovery. EmailAuthentication is the cheapest insurance; TOTP is fine too. Decide the break-glass path before you enforce anything — an artisan command or admin action that clears passkeys rows for a user is a two-line job and worth writing on day one.

Profile page is the registration surface — do not build your own. The plugin registers the management UI automatically once the plugin is on the panel and the model implements PasskeyUser. Adding a custom Livewire page to register passkeys means re-implementing the ceremony and its JS. Resist it. If you need to re-skin the profile page, keep the plugin's components mounted rather than replacing them.

Config consolidation when Fortify is present. If Fortify is in the app, WebAuthn settings live in config/fortify.php under passkeys, and config/passkeys.php is ignored entirely. Pick one home and delete or leave a comment in the other so the next developer doesn't tune the wrong file. This is the single most likely source of "I changed the RP ID and nothing happened."

Throttle tuning per environment. passkeys.throttle (default throttle:6,1) applies to every endpoint the plugin registers. Loosen it for local/staging so repeated manual testing doesn't 429 you mid-ceremony; leave production at the default or stricter. Never loosen it in production just to make a flaky E2E test pass.

Test with real authenticators, not just unit tests. WebAuthn is browser-and-OS-specific by nature. Playwright/Selenium can be configured with a virtual authenticator (WebAuthn.addVirtualAuthenticator in CDP), but you still need one manual pass on Touch ID and one on a hardware key before you consider a release done. Include "remove the only passkey, then try to log in" and "register two passkeys, remove one, verify the other still works" in the smoke checklist.

Gotchas and Tips

managementOnly() + isRequired: true traps users on the setup page. managementOnly() removes passkeys from the MFA challenge entirely while keeping the profile UI. Combine it with isRequired: true and Filament demands a factor the user has just registered but which is excluded from the challenge — an unresolvable loop. The README warns about this; treat it as a hard rule, not a soft one. If you need enforcement, drop managementOnly().

Without a fallback MFA provider, a lost device is a permanent lockout. No admin-side credential reset ships with the package. Write one before you need one:

// e.g. an artisan command or Filament admin action
$user->passkeys()->delete();   // verify the relation name against laravel/passkeys

Audit-log it. A silent credential wipe is a security event.

The User model change follows the model everywhere. PasskeyAuthenticatable is not panel-scoped. If the same User backs an API, a customer portal, or a mobile app, the trait's behavior and the passkeys table exist there too. This is the most common planning surprise — decide whether it's intended before adding the trait, because reverting later means unwinding the migration and any credentials users have registered.

RP ID and allowed origins must match the browser's reality, including port. localhost in dev, the real apex domain in prod, and every origin the panel is reachable at. A mismatch produces no error in PHP — the browser simply refuses to offer the credential, and you get a dead button on the login page. When debugging "no passkey prompt," open DevTools → Application → WebAuthn and inspect the request/response, not the Laravel log.

Fortify silently wins the config war. Already covered above, but repeated because it's the number-one integration failure: if Fortify is present, config/passkeys.php is dead weight and any changes there are no-ops. Search config/fortify.php first.

Pre-1.0 on both sides of the dependency chain. The plugin is v0.3.1; laravel/passkeys is ^0.4 and also pre-1.0. Neither promises API stability. Pin both in composer.json (^0.3.1 and ^0.4 rather than *), and treat any Filament minor bump as a release-gated change with a manual passkey smoke test — this plugin sits on Filament's auth/MFA extension points, which are exactly the surfaces that move between minors.

Three stars, zero dependents, single maintainer. No ecosystem validation. Budget for the possibility that upstream goes quiet. The upside: MIT license and a genuinely small code surface — the glue is a handful of Filament classes plus a Blade/Livewire view and a JS bundle. Forking is a realistic contingency, not a rewrite. Clone it, note where the profile component and the MFA provider are wired, and keep that mental map. If a Filament minor breaks the plugin, patching your fork is hours, not weeks.

Release metadata is unreliable — read the tags. The advertised "last release" date doesn't reconcile with the actual tag list (the newest tags are v0.3.1). Don't use the metadata as a maintenance signal; check the repo's commit activity and issue responsiveness directly before betting a security-critical path on it.

Audit logging is unstated — build it yourself. There is no documented event for passkey registration, assertion, or removal. If your threat model or compliance regime needs those, hook the model events on the passkeys relation or Laravel's auth events and ship your own log. Don't assume it exists.

Security review must cover laravel/passkeys, not just this plugin. Challenge generation, credential storage, signature verification, and the user-handle secret all live upstream. Reviewing only the Filament glue gives false confidence — the actual cryptography and storage are in the dependency, and so is the config you're about to tune. Scope the review accordingly.

Vietnamese translations exist alongside English; other locales fall back. If your panel is localized, check the passkey strings render as expected in your locale before release — untranslated keys surface on the login button and profile page, which are high-visibility surfaces.

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.
adachsoft/changelog-linter
adachsoft/collection
payerurl/telegrambot-crypto-payment-instant-payout
dskripchenko/laravel-hybrid-search
riorizkyrainey/lara-ced
mehedimi/static-vel
zharp/zharp
nonsapiens/bigquery-model-sync
lgu-bayambang-ict-office/lgu-laravel-development-standards
andydefer/laravel-support
se7enxweb/flysystem
lampwick/laravel
jeslage/statamic-emojis
micahdshackelford/rmap-laravel
draftsmaninc/draftsman
square-bit/laravel-volition
himito/laravel-mailman
htinlynn/genius-helper
maco-studios/hephaestus
elmogy/larafast