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.
What the package actually buys you. Filament panels ship with password auth and, since v4/v5, a pluggable multi-factor system. adriaanzon/filament-passkeys adds the one thing that is genuinely expensive to build in-house: a working WebAuthn ceremony (registration, challenge, assertion, verification) exposed through Filament's native extension points — a "Sign in with passkey" button and browser autofill on the login page, a passkey MFA provider that slots into ->multiFactorAuthentication(), and a profile-page management UI for registering and removing authenticators. For a TPM, the value proposition is not "passkeys" as a feature; it is phishing-resistant authentication delivered as configuration rather than a project. That is the difference between a two-day spike and a six-week security initiative with a WebAuthn library review, a browser-compatibility matrix, and a bespoke UI.
Architecture fit. The decomposition is correct and, importantly, thin. Security-critical logic (challenge generation, credential storage, signature verification, user-handle hashing) lives in laravel/passkeys, a first-party-adjacent Laravel package. This package contributes only panel wiring and Blade/Livewire UI. Three consequences matter for planning:
laravel/passkeys and its configuration, not just this plugin. Budget for that explicitly; it is the real review, and it is the item most likely to be skipped because the plugin looks small.PasskeyUser + PasskeyAuthenticatable to User is an application-wide change, not a panel-scoped one. If that model is shared with an API, customer portal, or mobile backend, the trait's behaviour follows it there. This is the single most common source of unplanned blast radius.->passwordlessLogin(), ->managementOnly(), and isRequired: are the levers, and they map cleanly onto a staged rollout (optional second factor → opt-in passwordless → enforced for privileged accounts). That flexibility is the main reason to prefer this over a bespoke build; it lets you ship value before you ship policy.Integration feasibility. Four mechanical steps — composer require, publish and run the upstream migration, add contract + trait to User, register the plugin on the panel — plus one genuine prerequisite: correct WebAuthn environment configuration (Relying Party ID, allowed origins, user-handle secret, throttle, timeout) in config/passkeys.php, or in config/fortify.php if Fortify is already present. That last point is a real trap: Fortify silently overrides config/passkeys.php, so an environment with Fortify enabled must be configured in exactly one place and that decision documented. Most integration failures in the wild will be RP-ID/origin mismatches and Fortify config drift, not PHP errors — which means the failure signature is "looks broken in staging, works locally," a classic time sink if not anticipated.
Technical risk. The risk profile is maturity and bus factor, not complexity:
v0.3.1). No ecosystem validation and no evidence of production use at scale.laravel/passkeys ^0.4 (v0.3.0 requires it). Two fast-moving majors in the dependency chain, one of them <1.0 with no API-stability promise. Every Filament minor bump becomes a re-test event you own.managementOnly() is incompatible with isRequired: true (users get stuck on the setup page), and without a fallback MFA provider a lost device means a hard lockout. Both are documented, which is good — but both are the kind of thing a rollout plan must encode, not assume away.None of this is disqualifying for an internal admin panel with a named owner. All of it is disqualifying for an unowned, internet-facing authentication path used by people who cannot call a helpdesk. The mitigation is cheap and should be decided up front: MIT license plus a small code surface means you can vendor or fork it. Estimate the fork as a few files of Filament glue against a stable upstream core — a maintainable contingency, not a rewrite.
Key questions to resolve before committing.
throttle:6,1 is the default; is that yours?)laravel/passkeys matrix, and who owns re-testing on Filament minor bumps?Stack fit. Target the happy path deliberately: a Laravel app on Filament v5, single panel, standard Authenticatable User, default guard, ->login() and ->profile() enabled, no custom login controller, no existing MFA provider. Fit degrades — and should trigger a review rather than a go-ahead — with multi-guard setups, heavily overridden profile pages, panels behind an SSO broker, or a User model shared with non-Filament surfaces.
Build vs. adopt framing for the gate. Three options belong on the table, not two: (a) adopt this package, (b) build passkey support directly on laravel/passkeys with a small in-house Filament UI, (c) adopt now with a standing fork option. Option (a) is roughly a day of integration plus review; option (b) is the same security review plus UI work, and is the natural fallback if the plugin's Filament coupling proves brittle. Presenting (b) alongside (a) is what makes the recommendation credible — it shows the plugin is buying convenience, not exclusivity.
Migration path. There is no data migration beyond the upstream passkeys table — no changes to existing credentials, no forced user action. That makes this additive and reversible: removing the plugin and the trait restores password-only login with no data loss beyond registered public keys (which are cheap to re-register, unlike shared secrets). Call this out explicitly; it is the strongest argument for proceeding and it lowers the approval cost considerably.
Compatibility. Confirm and record: PHP version floor, Laravel major, Filament exactly v5 (not v4 — the README's ->multiFactorAuthentication() and Filament\Auth\MultiFactor\... namespaces are v5-shaped), laravel/passkeys ^0.4, and browser support for WebAuthn conditional UI (autofill). Conditional UI is the one front-end capability that degrades silently in older browsers — plan for a graceful fallback to the explicit button rather than treating autofill as a requirement.
Sequencing. Recommended four-phase rollout, each phase independently valuable and independently abandonable:
EmailAuthentication as a fallback. Never ship PasskeyAuthentication alone — the README's lockout warning is the operative constraint here. Instrument login success/failure during this window.isRequired: true for a defined privileged subset only — and only after confirming that cohort is fully enrolled and that admin-side recovery exists.Do not compress phases 2 and 4. The gap between "optional second factor" and "enforced second factor" is where lockout incidents live, and it is also where you learn whether your recovery runbook is real.
Maintenance. Two dependency edges to watch: Filament (fast-moving major, minor bumps can break plugin wiring) and laravel/passkeys (<1.0, no stability promise). Assign a named owner and a concrete cadence — re-test on every Filament minor, and pin both dependencies rather than floating them. Budget is small but non-zero: an afternoon per Filament minor, plus a review whenever upstream tags. The fork contingency should be pre-scoped (which files, how many, who) rather than improvised during an incident.
Support. Two new user-facing failure classes enter the helpdesk: cannot enrol (browser/device/RP-ID issues, usually self-resolving) and cannot authenticate (lost device, synced passkey unavailable, wrong origin after a domain change). The second is a security incident class, not a support ticket, and needs a documented break-glass procedure with an approval step and an audit trail. Do not go live without a written recovery runbook, and do not let recovery be "an engineer edits the database." The most common operational mistake with passkey rollouts is shipping enrolment and authentication while leaving recovery undefined until the first lockout.
Scaling. Negligible infrastructure impact — no new services, no external calls beyond the browser ceremony, no per-user cost. The scaling vector that matters is organisational: every additional panel, tenant, or domain that needs passkeys multiplies the RP-ID/origin configuration surface, and origin changes are among the few things that can invalidate existing credentials. If multi-domain or white-label panels are on the roadmap, resolve the relying-party strategy before enrolment begins, not after.
Failure modes to pre-plan. Ranked by likely frequency × impact:
config/passkeys.php) and include it in deploy checklists.managementOnly() + isRequired: true misconfiguration — users trapped on the setup page; guard with a config test.Ramp-up. Genuinely low for a Laravel-fluent engineer: the README is accurate, the setup is four steps, and the configuration surface is small. The real ramp-up cost is not code — it is the security review of laravel/passkeys, the recovery runbook, and the RP-ID/origin decision. Frame the estimate that way to stakeholders: a day of engineering, and a week of process, with the process being the part that determines whether the rollout succeeds.
Bottom line for the decision meeting. The technical risk is maturity and bus factor, not complexity; the operational risk is recovery and configuration, not scale. Both are mitigable at low cost with a named owner, a phased rollout, a mandated fallback MFA provider, and a pre-scoped fork option. Approve for an internal Filament v5 admin panel under those conditions; decline as an unowned, internet-facing authentication path for users without a support channel.
How can I help you explore Laravel packages today?