jeffersongoncalves/filament-multifactor-passkeys
Add passkey (WebAuthn) multi-factor authentication to Filament panels. Users register passkeys on their devices and confirm sign-in with biometrics or a PIN instead of one-time codes, with simple setup, recovery, and per-user passkey management.
Architecture fit. This is a thin Filament panel plugin that delegates the security-critical work — WebAuthn registration ceremonies, credential storage, challenge generation, and assertion signature verification — to spatie/laravel-passkeys. That division of labor is exactly what a TPM wants to see in an auth dependency: the crypto lives upstream in a maintained vendor package, and this repository contributes the Filament-glue layer. Concretely, that means a service provider, a Filament plugin contract registration on the panel, a publishable config and views, translations, a login-flow hook for the MFA challenge, and a profile UI for passkey registration and deletion. The audit surface we own is small and idiomatic.
Fit is binary on stack: it is a Filament 5 plugin targeting Laravel 11/12 and PHP 8.2+. If our admin panel is on Filament 3 or 4, or the app is a non-Filament Livewire/Inertia frontend, this package is out of scope and we should not spend a spike on it. If we are already on Filament 5, the architecture is a near-perfect match for "add phishing-resistant MFA to the highest-privilege surface with minimal bespoke code."
There is one important framing correction for stakeholders: the description markets this as a second factor alongside passwords, but the underlying Spatie package and the WebAuthn ceremonies it enables are also the foundation of passwordless login. The plugin's advertised mode is additive MFA, which is the safer product target — but the ambiguity matters for scoping, because "add a second factor" and "replace the password" are very different rollouts.
Integration feasibility. Installation is nominally a same-day task: composer require, register the plugin in the panel provider, run migrations, publish config and views, set relying-party ID and origin. The true feasibility risk is not package mechanics but deployment configuration. Every environment needs valid TLS with an RP ID scoped to the exact origin, and APP_URL must match. Subdomain topology (admin.example.com vs app.example.com) forces an explicit decision: one shared RP ID or per-panel configuration. Local development requires localhost or a tunnel with a matching origin. These are known, solvable problems, but they are infrastructure and platform-config work that will not appear in a composer-install estimate, and they are the single most common first-day failure mode.
The second feasibility constraint is the enrollment precondition: a user must register a passkey before any passkey-based challenge can succeed. That means the rollout has a chicken-and-egg shape — the feature is useless until enrollment is broad, and enrollment must be prompted through some channel (UI nudge, admin mandate, onboarding step). This is a product sequencing problem more than an engineering one, and it needs to be planned before install, not after.
Technical risk.
Key questions for the author / our own review.
userVerification / residentKey / authenticatorAttachment settings are actually exposed in the publishable config vs. hardcoded upstream?Stack fit. Laravel 11/12, PHP 8.2+, Filament 5, Livewire/Alpine for the browser ceremony, spatie/laravel-passkeys for persistence and challenge handling, plus openssl (typically gmp/bcmath) and valid TLS with APP_URL matching the RP origin. If any of those are not true today, stop here — this is not a package we can bend onto an older stack. For a TPM, the practical filter is: confirm Filament 5 first, everything else second. A single composer show filament/filament answers go/no-go before any engineering time is spent.
Migration path. Do not flip the panel to passwordless, and do not make passkeys mandatory on day one. Sequence it as four deliberate stages:
This ordering eliminates the entire lockout class of incident during rollout — the failure that turns a security win into an executive escalation.
Compatibility. Before committing, verify: the exact Filament 5 minor is pinned and matched; the panel has no competing auth customization that hooks the same login flow (OIDC/SSO, LDAP, custom login overrides, Fortify); the domain strategy supports the RP ID topology the plugin assumes; the Spatie migration table name does not collide with anything existing; and the spatie/laravel-passkeys version resolved by composer is the one whose auth path we read. Also confirm the Packagist version line matches the release notes before pinning.
Sequencing. Plan roughly five weeks to a role-based rollout, with the first week being the decision point:
Maintenance. Treat this as an internally owned component with an upstream benefactor, not as a vendor dependency. Budget 2–4 hours per month for dependency bumps, plus a real spike each time Filament ships a major. Pin exact versions in the lockfile, enable automated dependency PRs, and subscribe to security advisories for spatie/laravel-passkeys and its WebAuthn dependency — the advisories that matter will not arrive via this repository. The good news is the surface we own is small enough that patching or forking is measured in days, not quarters. The bad news is that "small enough to fork" is the same property as "you will probably have to."
Support. The dominant operational cost is not software, it is the helpdesk. Before the privileged-role rollout goes live, we need four things in writing and one thing built:
Without these in place, a single locked-out executive becomes an incident, and the passkey rollout gets blamed for it.
Scaling. Runtime cost is negligible. Assertion is a database lookup by credential ID plus a signature verification — no per-request crypto scaling concern, no meaningful added latency, no new stateful service. The DB read is indexed; the signature verify is microseconds to low milliseconds. At our panel's user volumes this is a rounding error against the existing session and query cost. Scaling concerns live entirely in support and onboarding, not in compute.
Failure modes. Rank by probability × blast radius:
APP_URL consistency in a deploy-time health check, not in a README.Ramp-up. For the implementing engineer: half a day to install and configure in sandbox, one to two days to read the plugin end-to-end plus the upstream assertion path, and two to three days to work through cross-platform enrollment and recovery. Call it one engineer-week to confident. The ramp-up cost is not learning WebAuthn — the package deliberately hides that — it is learning the deployment topology (RP ID, origins, TLS, tunnels) and the browser support reality. That knowledge is non-transferable from the package docs, which is why the spike and pilot are load-bearing rather than ceremonial.
For the broader org: the helpdesk needs a short training session and the runbook; users need one clear page explaining what a passkey is, why they are being asked, and what to do if they lose a device. Budget that communications work explicitly — it is the difference between a security improvement users accept and a security improvement users route around.
How can I help you explore Laravel packages today?