Weave Code
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 Laravel Package

laravel/passkeys

Add passwordless WebAuthn/passkey authentication to Laravel. Install migrations, add a trait/contract to your User model, and use the @laravel/passkeys JS client for registration and login. Includes built-in routes for login, confirmation, and passkey management.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup for First Use Case (Passwordless Login)

  1. Install the package:

    composer require laravel/passkeys
    php artisan vendor:publish --tag=passkeys-migrations
    php artisan migrate
    
  2. Add the trait to your User model:

    use Laravel\Passkeys\PasskeyAuthenticatable;
    
    class User extends Authenticatable
    {
        use PasskeyAuthenticatable;
    }
    
  3. Frontend integration (using @laravel/passkeys npm package):

    import { Passkeys } from '@laravel/passkeys';
    
    // Login flow
    const options = await Passkeys.login();
    const credential = await Passkeys.verify(options);
    
  4. Test the login route:

    • Visit /passkeys/login/options (returns WebAuthn options for the client).
    • Submit the assertion to /passkeys/login (handles authentication).

First Use Case: Replace Email/Password Login

  • For developers: Replace your existing login form with the passkey flow. The package handles:
    • Generating WebAuthn challenges.
    • Validating assertions.
    • Logging the user in via Laravel’s auth system.
  • For users: No password management—just register a passkey (e.g., via Touch ID) and log in with a single tap.

Implementation Patterns

Workflow: Full User Journey

  1. Registration (Authenticated User):

    • User visits /user/passkeys/options → Client generates a new credential → Submit to /user/passkeys → Passkey stored in DB.
    • Pattern: Use Passkeys::register() in a controller or directly call the POST /user/passkeys route.
  2. Login (Guest):

    • User visits /passkeys/login/options → Client shows available credentials → Submit assertion to /passkeys/login → User authenticated.
    • Pattern: Leverage the PasskeyVerified event to trigger post-login actions (e.g., redirect, send welcome email).
  3. Reauthentication (2FA-like Confirmation):

    • Use GenerateVerificationOptions with an authenticated user to scope credentials to that user:
      $options = app(GenerateVerificationOptions::class)($request->user());
      
    • Submit to VerifyPasskey with the user object to enforce ownership:
      $passkey = app(VerifyPasskey::class)(
          $request->credential(),
          $options,
          $request->user()
      );
      
    • Use case: Confirm sensitive actions (e.g., password changes, admin actions).
  4. Passkey Management:

    • List passkeys: Query the passkeys table (or your custom model).
    • Delete a passkey: DELETE /user/passkeys/{id} (triggers PasskeyDeleted event).
    • Pattern: Use middleware like password.confirm to protect management routes.

Integration Tips

  • Hybrid Auth: Combine with existing email/password login. Use middleware to route users to passkey flows:
    Route::middleware(['guest'])->group(function () {
        Route::get('/login', function () {
            return redirect()->route('passkeys.login.options');
        });
    });
    
  • Custom Guard: Override the guard in config to use a custom auth guard (e.g., api):
    'guard' => 'api',
    
  • Frontend State Management: Use the @laravel/passkeys npm package to manage WebAuthn state (e.g., store challenges in localStorage).
  • Fallback Flows: Handle cases where passkeys fail (e.g., redirect to email/password):
    Passkeys::authorizeLoginUsing(function ($request, $user, $passkey) {
        if (!$passkey) {
            return redirect()->route('login.email');
        }
        return true;
    });
    

Common Patterns

Pattern Implementation Use Case
Passkey Registration POST /user/passkeys with WebAuthn attestation response. Onboarding new users.
Passkey Login POST /passkeys/login with WebAuthn assertion response. Guest authentication.
Reauthentication VerifyPasskey with an authenticated user to scope credentials. Admin dashboards, sensitive actions.
Passkey Deletion DELETE /user/passkeys/{id} with WebAuthn assertion for confirmation. User account cleanup.
Event Listening Listen to PasskeyVerified to trigger post-login logic. Analytics, notifications.
Custom Middleware Add auth.passkey middleware to protect routes. Role-based access control.

Gotchas and Tips

Pitfalls

  1. Opaque User Handles:

    • The package generates stable opaque handles for users (never exposing primary keys to WebAuthn). If you override getPasskeyAuthenticatorIdentifier(), ensure the handle is stable (e.g., hashed user_id + secret).
    • Fix: Use the default implementation or ensure your custom handle doesn’t change for a user.
  2. Database Transactions:

    • Passkey verification uses pessimistic locking to prevent race conditions. If your database lacks transactions (e.g., SQLite in memory mode), this will fail.
    • Fix: Use a transactional database (MySQL, PostgreSQL) or disable locking in config (not recommended):
      'transactional_verification' => false,
      
  3. Allowed Origins:

    • The allowed_origins config must include all domains where passkeys are registered/verified (e.g., app.example.com, staging.example.com).
    • Fix: Add all origins to the config array or use env() to dynamically set them.
  4. Middleware Misconfiguration:

    • The management_middleware config defaults to ['password.confirm']. If you set it to [], passkey management routes will be unprotected.
    • Fix: Explicitly define middleware (e.g., ['auth', 'verified']).
  5. Custom Model Route Bindings:

    • If you use custom User or Passkey models, ensure their route keys match the package’s expectations (e.g., id for Passkey).
    • Fix: Override getRouteKeyName() in your custom models if needed.
  6. WebAuthn Timeout:

    • The default timeout is 60 seconds. If users experience delays (e.g., slow networks), they may get NotAllowedError.
    • Fix: Increase the timeout in config:
      'timeout' => 120000, // 2 minutes
      
  7. Resident Key Requirement:

    • The package defaults to requiring resident keys (passkeys stored in the OS keychain). Some authenticators (e.g., YubiKeys) may not support this.
    • Fix: Adjust authenticatorSelection in a custom GenerateRegistrationOptions action.
  8. Passkey Deletion:

    • Deleting a passkey requires reauthentication (to prevent unauthorized deletions). If you bypass this, users could lose access.
    • Fix: Always use the DELETE /user/passkeys/{id} route with WebAuthn assertion.

Debugging Tips

  1. WebAuthn Errors:

    • Check the error field in the response for WebAuthn-specific errors (e.g., NotAllowedError, BadCredentialError).
    • Tool: Use the WebAuthn Debugger to validate options/assertions.
  2. Database Locking:

    • If passkey verification fails with Lock wait timeout, increase your database’s innodb_lock_wait_timeout (MySQL) or lock_timeout (PostgreSQL).
  3. Event Debugging:

    • Listen to events in AppServiceProvider to log passkey operations:
      public function boot()
      {
          PasskeyVerified::listen(function ($user, $passkey) {
              Log::info("Passkey verified for user {$user->id}", ['passkey_id' => $passkey->id]);
          });
      }
      
  4. Custom Actions:

    • Override actions in a service provider to debug WebAuthn logic:
      $this->app->bind(GenerateRegistrationOptions::class, function () {
          return new class extends GenerateRegistrationOptions {
              public function authenticatorSelection()
              {
                  $criteria = parent::authenticatorSelection();
                  Log::debug('Authenticator selection:', $criteria->toArray());
                  return $criteria;
              }
          };
      });
      

Extension Points

  1. Custom Passkey Model:
    • Extend the base Passkey model to add custom fields (e.g., device_type, last_used_at):
      class Passkey extends \Laravel\Passkeys\Passkey
      
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.
calmfox/watch-sylius
damienfern/grpc-symfony-bundle
atoolo/index-bundle
atoolo/genai-bundle
coprotoai/laravel-ticket
davidjln/llm-carbon-bundle
cryonighter/valid-request-bundle
coolms/taxonomy-bundle
coolms/field-bundle
articulate-orm/symfony
aaix/laravel-tall-architect
ephoto/akeneo-connector
emmanuelballery/eb-plantumlbundle
emielburgman/symfony-visitor-beacon
emielburgman/symfony-visit-storage
emielburgman/symfony-security-headers
emielburgman/symfony-log-viewer
emarref/xdebug-bundle
emarref/pubnub-bundle
elriseio/finance-money-bundle