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

Webauthn Framework Laravel Package

web-auth/webauthn-framework

PHP library for adding WebAuthn/FIDO2 support to your app: register and authenticate users with passkeys, security keys, and biometrics. Includes framework integrations, attestation and assertion validation, and a clean API for passwordless login.

View on GitHub
Deep Wiki
Context7

Getting Started

The web-auth/webauthn-lib package is framework-agnostic — there is no Laravel service provider, config file, or facade. You wire it yourself. Budget an hour for the first integration; most of the work is Laravel-side plumbing, not library API.

composer require web-auth/webauthn-lib
# frontend helper (optional, pairs with the ceremony flow)
npm i @web-auth/webauthn-stimulus

Requirements worth checking before you start:

  • PHP 8.1+ (5.x line), ext-openssl, ext-sodium, ext-mbstring.
  • HTTPS in every environment except localhost. The RP ID must be a registrable suffix of the origin's host. This trips people up on 127.0.0.1 vs localhost and on staging domains behind a proxy.
  • A persistent store for credential sources (an Eloquent model is fine) and a store for the in-flight challenge (session or cache).

Where to look first, in order:

  1. Webauthn\PublicKeyCredentialCreationOptions / PublicKeyCredentialRequestOptions — the two DTOs you serialize to the browser.
  2. Webauthn\CeremonyStep\CeremonyStepManagerFactory — in 5.x this replaces the old Webauthn\Server class (removed). It builds the creation and assertion step managers.
  3. Webauthn\AuthenticatorAttestationResponseValidator and AuthenticatorAssertionResponseValidator — the two classes that actually verify responses.
  4. Webauthn\PublicKeyCredentialSourceRepository — the interface you must implement to persist credentials.

First use case to build: register a security key for an already-authenticated user, then verify the attestation. Do not start with passkey login (discoverable credentials) — it requires solving user-handle lookup first.

Minimal bootstrapping, typically in a small service class bound in a service provider:

use Webauthn\CeremonyStep\CeremonyStepManagerFactory;

$factory = new CeremonyStepManagerFactory();
$factory->setAllowedOrigins([config('app.url')]);

$creation  = $factory->creationCeremony();
$assertion = $factory->requestCeremony();

Implementation Patterns

Two endpoints per ceremony, stateless HTTP. Registration is GET /webauthn/register/options → browser navigator.credentials.create() → POST /webauthn/register/verify. Authentication mirrors it. Keep the ceremony state (challenge, user handle, RP entity) server-side between the two calls.

Challenge storage is your responsibility. The library does not persist challenges. The idiomatic Laravel pattern is a cache entry keyed by ceremony and user:

Cache::put("webauthn:reg:{$user->id}", $options, now()->addMinutes(5));

Use the session for classic server-rendered apps; use the cache (or a dedicated webauthn_challenges table) for stateless/API apps where sessions are unavailable. Always delete the challenge on read — it is single-use.

Repository implementation. Wrap an Eloquent model in a class implementing PublicKeyCredentialSourceRepository. Store at minimum: credential ID (binary, base64url-encode for the column if you prefer), public key, sign counter, user handle, transports, AAGUID, attestation type, and a backup_eligible flag for passkeys. Type the PublicKeyCredentialSource round-trip carefully — jsonSerialize()/jsonDeserialize() on the source object is the safest way to persist without pinning your schema to internal properties.

Generation options are per-user, never cached across users. Build PublicKeyCredentialUserEntity from your User model, but use a stable, opaque, non-PII user handle (a UUID), not the email or auto-increment ID. Rotating the user handle orphans existing passkeys.

For passkeys/conditional UI, set ->setAuthenticatorSelection(AuthenticatorSelectionCriteria::create()->setResidentKey(ResidentKeyRequirement::REQUIRED)->setUserVerification(UserVerificationRequirement::REQUIRED)) and pass an empty allowCredentials on the assertion options. You then need a userHandle → user lookup before you can verify; the library returns the user handle but will not resolve it for you.

Counter handling. Read the counter from the verification result and update your row. Do not hard-fail on 0: synced passkeys legitimately report a static counter, and many platform authenticators never increment it. Log and flag rather than lock out.

Frontend. The @web-auth/webauthn-stimulus controllers accept the JSON-encoded options directly, so the cleanest pattern is to return the options DTO from a controller and let jsonSerialize() produce the payload. Avoid hand-rolling base64url encoding — the library's normalizers already emit the correct browser format, and re-implementing it is the most common source of NotSupportedError in the browser.

Testing. Ceremony responses are large binary blobs; don't hand-craft them. Extract one real attestation/assertion JSON from a browser run into a fixture and unit-test your repository and controller around it. Fake the challenge generator in tests so assertions are deterministic.

Gotchas and Tips

5.3.9 tightened two transitive dependency constraints — cbor-php is now ^3.4 and cose-lib is ^4.8. If you have either package required explicitly (common when you've worked around an encoding bug, or when another package pins them), run composer why cbor-php before upgrading and expect a resolution conflict if you're below those floors. There are no API changes in this release, so the upgrade itself is safe; the risk is entirely in dependency resolution. It's also a security-flagged patch, so don't sit on it.

The Webauthn\Server class no longer exists in 5.x. Every tutorial and blog post older than roughly 2023 uses new Server(...), $server->generatePublicKeyCredentialCreationOptions(), and $server->loadAndCheckAttestationResponse(). None of that works. Translate to CeremonyStepManagerFactory + the two validators.

Origin validation fails behind load balancers. setAllowedOrigins() compares against the actual request origin the browser sends, so that part is fine — but if you're deriving the RP ID or allowed origins from config('app.url') and your app sits behind a proxy that rewrites the scheme, you'll get mismatches. Prefer an explicit env var (WEBAUTHN_RP_ID, WEBAUTHN_ORIGIN) over app.url.

Credential IDs are binary. Storing them in a varchar column without base64url-encoding produces silent truncation on some MySQL configurations. Encode on write, decode on read, and add a unique index.

Exception handling is a design decision. The library throws granular exceptions (Webauthn\Exception\*) with technical detail. Catch them at the controller boundary, log the full exception, and return a flat 422 with a generic message — the exception text leaks relying-party configuration and is not user-actionable.

Don't validate userVerification you didn't ask for. If your creation options set UserVerificationRequirement::PREFERRED, some authenticators will return UP only. Requiring UV at the assertion step when you didn't request it at creation is a classic inconsistent-config bug that only shows up on certain hardware.

Extension points, in rough order of usefulness: a custom CeremonyStep to inject your own logging or IP-binding policy into the validation chain; a MetadataStatementRepository wired to the FIDO MDS for attestation trust decisions (only worth it in regulated/enterprise contexts — it adds a network dependency and a refresh job); per-authenticator overrides via AuthenticatorAttestationResponseValidator subclasses, which is rarely needed and brittle across minor versions.

Version pinning. The 5.3.x line has shipped frequent patch releases that touch dependency floors. Pin the minor (^5.3) rather than the patch, and let CI catch resolution conflicts on a schedule rather than during a deploy.

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.
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings
coleus/apps
seampact/laravel
emmyconceptng/laravel-ng-postcode
jjsquady/round-robin
skyraptor/chatter
edlugz/sasapay
abbasudo/artisan-route