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

Laravel Passkeys Laravel Package

spatie/laravel-passkeys

Add passkey (WebAuthn) login to Laravel without passwords. Includes Livewire components to register/generate passkeys and a Blade component to authenticate users using device-stored credentials (1Password, macOS Keychain, etc.).

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Installation

    composer require spatie/laravel-passkeys
    

    Ensure Livewire is installed (composer require livewire/livewire).

  2. Publish Config

    php artisan vendor:publish --provider="Spatie\Passkeys\PasskeysServiceProvider" --tag="passkeys-config"
    

    Update config/passkeys.php with your app’s relying party details (e.g., rp_name, rp_id).

  3. Run Migrations

    php artisan migrate
    

    Creates a passkeys table to store credentials.

  4. First Use Case: Registration Add the Livewire component to your registration form:

    <x-passkeys::register />
    

    This renders a passkey registration UI (e.g., "Add a Passkey" button).


Key Classes to Know

  • Passkey Model: Stores passkey credentials (extends Spatie\Passkeys\Models\Passkey).
  • PasskeyManager: Core logic for generating/verifying passkeys (injected via Laravel’s service container).
  • Livewire Components:
    • RegisterPasskey: Handles passkey creation.
    • AuthenticateWithPasskey: Handles authentication.

Quick Authentication Flow

  1. Trigger Authentication:
    <x-passkeys::authenticate />
    
  2. Verify in Controller:
    use Spatie\Passkeys\Facades\Passkey;
    
    if (Passkey::authenticate($request)) {
        // User authenticated via passkey
        return redirect()->intended('/dashboard');
    }
    

Implementation Patterns

1. Passkey Registration Workflow

Livewire Integration

  • Use <x-passkeys::register /> in your registration form.
  • Customize the register component’s props (e.g., user, redirectTo):
    <x-passkeys::register
        :user="$user"
        redirectTo="/dashboard"
    />
    
  • Event Handling: Listen for PasskeyRegistered events to trigger post-registration actions (e.g., send welcome email):
    use Spatie\Passkeys\Events\PasskeyRegistered;
    
    PasskeyRegistered::listen(function (Passkey $passkey) {
        // Send email or log activity
    });
    

Customizing Registration Options

Override the GeneratePasskeyRegisterOptions class to modify passkey behavior (e.g., enforce authenticator attachment):

use Spatie\Passkeys\PasskeyRegisterOptions;

class CustomPasskeyRegisterOptions extends PasskeyRegisterOptions
{
    public function __construct()
    {
        parent::__construct();
        $this->authenticatorSelection->requireResidentKey();
        $this->authenticatorSelection->requireUserVerification();
    }
}

Register it in config/passkeys.php:

'register_options' => \App\CustomPasskeyRegisterOptions::class,

2. Authentication Flow

Blade Component

Embed <x-passkeys::authenticate /> in your login form. Supports:

  • remember: Boolean to enable "Remember Me" (default: false).
  • redirectTo: Post-authentication redirect URL.

Controller Logic

Verify passkey authentication in your login controller:

public function login(Request $request)
{
    if (Passkey::authenticate($request)) {
        $request->session()->regenerate();
        return redirect()->intended('/dashboard');
    }

    // Fallback to password auth
    // ...
}

Handling Multiple Passkeys

Fetch a user’s passkeys:

$user->passkeys; // Collection of Passkey models

Delete a passkey:

$user->passkeys()->where('id', $passkeyId)->delete();

3. Inertia/Vue.js Integration

Pass passkey data to Inertia:

return Inertia::render('Auth/Login', [
    'passkeys' => auth()->user()->passkeys->map(fn ($passkey) => [
        'id' => $passkey->id,
        'name' => $passkey->name,
        'type' => $passkey->type,
    ]),
]);

Use the webauthn library in Vue to handle passkey operations client-side.


4. Testing Passkeys

Use the Passkey facade in tests:

use Spatie\Passkeys\Facades\Passkey;

public function test_passkey_authentication()
{
    $user = User::factory()->create();
    $passkey = Passkey::createForUser($user);

    $response = Passkey::authenticate(
        new Request([
            'passkey' => $passkey->toArray(),
        ])
    );

    $this->assertTrue($response->authenticated());
}

Gotchas and Tips

1. Configuration Pitfalls

  • Relying Party ID (rp_id): Must match your domain exactly (e.g., https://your-app.com). Use spatie/laravel-passkeys:generate-rp-id Artisan command to generate it:

    php artisan passkeys:generate-rp-id
    

    Gotcha: If rp_id mismatches, browsers will reject passkey operations.

  • Allowed Origins: Configure allowed_origins in config/passkeys.php to restrict passkey usage to specific domains:

    'allowed_origins' => ['https://your-app.com', 'https://staging.your-app.com'],
    

    Tip: Use null to allow all origins (not recommended for production).


2. Debugging Common Issues

Issue Solution
Passkey registration fails Check browser console for NotAllowedError (likely rp_id mismatch).
Authentication timeouts Ensure authenticateWithPasskey middleware is not interfering.
Livewire component not rendering Verify Livewire is installed and the component is properly namespaced.
"No passkeys found" Check if the passkeys table has records for the user.

Debugging Tip: Enable WebAuthn logging in config/passkeys.php:

'debug' => env('PASSKEYS_DEBUG', false),

Logs will appear in Laravel’s log files.


3. Extension Points

Custom Passkey Model

Extend the Passkey model to add custom fields:

use Spatie\Passkeys\Models\Passkey as BasePasskey;

class Passkey extends BasePasskey
{
    protected $casts = [
        'metadata' => 'json',
        'created_at' => 'datetime:Y-m-d H:i:s',
    ];

    public function user()
    {
        return $this->belongsTo(User::class);
    }
}

Update config/passkeys.php:

'model' => \App\Models\Passkey::class,

Custom Events

Extend the PasskeyRegistered event to add custom logic:

use Spatie\Passkeys\Events\PasskeyRegistered;

PasskeyRegistered::listen(function (Passkey $passkey) {
    // Example: Log passkey creation
    \Log::info("Passkey registered for user {$passkey->user->email}");
});

4. Security Considerations

  • Backup Codes: Implement a backup code system for passkey recovery (not built into the package).
  • Rate Limiting: Protect passkey endpoints with Laravel’s throttle middleware:
    Route::middleware(['throttle:10,1'])->group(function () {
        Route::post('/passkey/authenticate', [PasskeyController::class, 'authenticate']);
    });
    
  • User Verification: Enforce requireUserVerification() in PasskeyRegisterOptions to prevent silent authentications.

5. Performance Tips

  • Caching: Cache passkey operations if users frequently register/authenticate:
    $passkey = Cache::remember("passkey_{$user->id}", now()->addHours(1), function () use ($user) {
        return $user->passkeys()->latest()->first();
    });
    
  • Lazy Loading: Use with() to eager-load passkeys in queries:
    $user = User::with('passkeys')->find($id);
    

6. Browser Compatibility

  • Supported Browsers: Chrome, Edge, Safari (latest versions). Firefox support is improving but may require polyfills.
  • Fallback UI: Provide a "Forgot Password?" link alongside passkey options for unsupported browsers.

7. Migration Tips

  • Existing Users: Migrate password-based users to passkeys gradually:
    User::where('email_verified_at', null)->chunk(100, function ($users) {
        foreach ($users as $user) {
            Passkey::createForUser($user);
        }
    });
    
  • Database Schema: If extending the passkeys table, run:
    php artisan migrate --path=/vendor/spatie/laravel-passkeys/database/migrations
    
    Then add your custom migrations.
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.
hamzi/corewatch
minionfactory/raw-hydrator
hexters/coinpayment
rjcodes/rjcms
act-training/laravel-permissions-manager
alimarchal/laravel-chart-of-accounts
babenkoivan/elastic-scout-driver
mkwebdesign/filament-watchdog-v5
renatomarinho/laravel-page-speed
zedmagdy/filament-business-hours
renatovdemoura/blade-elements-ui
devgeek/beacon-admin
benjamin-rqt/data-watcher-bundle
atriumphp/atrium
sandermuller/package-boost-laravel
sandermuller/boost-skills
redaxo/core
yusufgenc/filament-api-forge
l3aro/rating-star-for-filament
leek/filament-subtenant-scope