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

Passkeys For Laravel Laravel Package

roundly-consulting/passkeys-for-laravel

Add passkey (WebAuthn) authentication to Laravel apps. Register and verify passkeys so users can sign in with Face ID, Touch ID, or a security key instead of a password.

View on GitHub
Deep Wiki
Context7

Getting Started

  • composer require roundly-consulting/passkeys-for-laravel. The service provider auto-discovers; if you use dont-discover or register providers by hand, add it to bootstrap/providers.php (Laravel 11+) or config/app.php.
  • Publish and read the config and migration stubs before touching anything else (php artisan vendor:publish — check the README for the exact tags). The config is the map: relying-party ID/origins, route registration, and where the user handle comes from.
  • Add the user-handle column using the package's schema macro rather than a hand-rolled column:
    Schema::table('users', function (Blueprint $table) {
        $table->passkeyUserHandle();
    });
    
  • Attach the package's trait to your authenticatable model, then php artisan migrate.
  • First use case worth building end-to-end: enrol a passkey for a logged-in user, then authenticate with it in a fresh incognito window. That loop exercises schema, routes, the JSON ceremony, and session handling in one go — if it works, the rest is UI.

Implementation Patterns

Treat the ceremony as two endpoints, not one. The registration/assertion options endpoint mints the challenge and returns PublicKeyCredentialCreationOptions / PublicKeyCredentialRequestOptions; the browser calls navigator.credentials.create() or .get(); your verify endpoint posts the result back. Keep both as thin JSON controllers — put your own policy (who may enrol, how many credentials) in a form request or action class, not in the ceremony.

Layer it onto existing auth rather than replacing it. The pragmatic rollout is: password login stays, and after a successful login you offer "add a passkey" from the account settings page. Offer passkey login on the login screen only when PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable() is true, and keep a recovery path (password reset, magic link, recovery codes) from day one — users lose devices.

Support multiple credentials per user. Enrolment is per-device, so your settings screen needs a list, human-readable labels, last_used_at, and a delete action. Refuse to delete the last credential if passkeys are the user's only login method.

Testing. Feature-test the options endpoint for shape (challenge present and non-trivial, rp.id matches config) and feature-test verify with a deliberately malformed/failed assertion — asserting the user is not authenticated. Real ceremonies belong in a browser test; drive Chrome via CDP WebAuthn.addVirtualAuthenticator (Dusk/Playwright) rather than hand-crafting attestations.

Keep static analysis in CI. Put larastan and phpstan/extension-installer in require-dev so migrations using the macro type-check without extra wiring (see below).

Gotchas and Tips

  • The 1.1.3 fix is about static analysis, not runtime. The passkeyUserHandle() macro only exists once the provider boots. Larastan usually boots an auto-discovered host app and finds it, but not under Testbench package development, dont-discover, or a manually registered provider — there migrations fail PHPStan with "Call to an undefined method …Blueprint::passkeyUserHandle()". 1.1.3 ships an extension loaded automatically by phpstan/extension-installer via extra.phpstan.includes.
  • Without the installer you must include it yourself: add vendor/roundly-consulting/passkeys-for-laravel/extension.neon to the includes: of your phpstan.neon. If analyses pass locally but fail in CI, suspect composer install --no-plugins / --no-scripts silencing extension-installer.
  • Package development under Testbench: list the provider in testbench.yaml so the macro is registered before your package's own migrations are analysed.
  • Same failure mode at runtime for anything that runs migrations without booting the app (raw scripts, external tooling). php artisan migrate is fine.
  • Relying-party config is origin-bound. localhost and 127.0.0.1 are different origins; staging and production each need their own RP ID and allowed origins or credentials silently fail to validate. php artisan config:clear after changing them.
  • The user handle is opaque and immutable. Don't derive it from email or a sequential ID; if you rotate it, every credential registered against the old handle is orphaned.
  • Challenge hygiene: single-use, short TTL, bound to the session/user. A replayable challenge defeats the whole protocol.
  • Migrate before hitting endpoints — unpushed or unrun migrations surface as 500s on the verify route, which is easy to misread as a WebAuthn failure.
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.
ignibyte/aic-framework
glavweb/data-schema-bundle
glavweb/datagrid-bundle
tacticmedia/scim-server
uni-method/frame
neika-ai/sdk
adachsoft/php-code-style
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
adachsoft/api-version-bundle
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox