DEV Community

Cover image for Zero-Password Security: Implementing the Official Laravel Passkeys Stack
Michael Laweh
Michael Laweh

Posted on Originally published at klytron.com

Zero-Password Security: Implementing the Official Laravel Passkeys Stack

Password-based authentication is a legacy security model. Phishing, credential stuffing, and weak password management continue to be the primary attack vectors for modern web applications.

To solve this, the technology industry has rallied around WebAuthn and Passkeys—a cryptographic standard enabling users to log in using biometric sensors (such as TouchID or FaceID), PINs, or physical security keys. Laravel has made this standard accessible by releasing laravel/passkeys on the backend, alongside @laravel/passkeys on the client.

Here is how to set up the official Laravel passkeys stack in your application.

How Passkeys Work Under the Hood

Unlike passwords, passkeys rely on public-key cryptography:

  • Registration: The user's device generates a unique cryptographic key pair. The private key remains securely stored on the device's hardware enclave, while the public key is sent to the Laravel application.
  • Authentication: The Laravel server sends a challenge. The device signs this challenge using its private key (after biometric verification) and returns it. The server verifies the signature using the stored public key.

At no point does the server store or receive private keys, biometrics, or passwords.

Server-Side Installation

Start by pulling in the first-party server-side package:

composer require laravel/passkeys
Enter fullscreen mode Exit fullscreen mode

Next, publish the database migrations:

php artisan vendor:publish --tag=passkeys-migrations
Enter fullscreen mode Exit fullscreen mode

Run the migrations to create the passkeys table, which will store the public credentials associated with your users:

php artisan migrate
Enter fullscreen mode Exit fullscreen mode

Implementing the User Contract

Your User model must implement the PasskeyUser contract and utilize the PasskeyAuthenticatable trait:

namespace App\Models;

use Illuminate\Foundation\Auth\User as Authenticatable;
use Laravel\Passkeys\Contracts\PasskeyUser;
use Laravel\Passkeys\Traits\PasskeyAuthenticatable;

class User extends Authenticatable implements PasskeyUser
{
    use PasskeyAuthenticatable;

    // ...
}
Enter fullscreen mode Exit fullscreen mode

Integrating with Laravel Fortify

If you use Laravel Fortify or Jetstream, passkey support can be enabled directly within config/fortify.php under the features array:

'features' => [
    Features::registration(),
    Features::resetPasswords(),
    Features::passkeys(), // Enable official passkey authentication
],
Enter fullscreen mode Exit fullscreen mode

Frontend Integration with the JS SDK

To handle the browser-level WebAuthn ceremonies, Laravel provides a companion NPM library. Install it via your package manager:

npm install @laravel/passkeys
Enter fullscreen mode Exit fullscreen mode

Registering a New Passkey

To allow a logged-in user to create a new passkey, import the registration helper and call it using Vanilla JS:

import { registerPasskey } from '@laravel/passkeys';

const createPasskeyButton = document.getElementById('create-passkey');

createPasskeyButton.addEventListener('click', async () => {
    try {
        // 1. Request options from the Laravel backend
        const response = await fetch('/passkeys/register-options');
        const options = await response.json();

        // 2. Trigger biometric verification on the device
        const credential = await registerPasskey(options);

        // 3. Send the public key credential back to Laravel
        const saveResponse = await fetch('/passkeys/register', {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
                'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').getAttribute('content')
            },
            body: JSON.stringify(credential)
        });

        if (saveResponse.ok) {
            alert('Passkey registered successfully!');
        }
    } catch (error) {
        console.error('Passkey registration failed:', error);
    }
});
Enter fullscreen mode Exit fullscreen mode

Authenticating a User

To log a user in without a password, implement the login flow:

import { authenticatePasskey } from '@laravel/passkeys';

const loginButton = document.getElementById('login-passkey');

loginButton.addEventListener('click', async () => {
    const email = document.getElementById('email').value;

    try {
        // 1. Fetch authentication challenge for this email
        const response = await fetch('/passkeys/login-options', {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({ email })
        });
        const options = await response.json();

        // 2. Perform WebAuthn browser challenge
        const assertion = await authenticatePasskey(options);

        // 3. Submit assertion to login route
        const loginResponse = await fetch('/passkeys/login', {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
                'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').getAttribute('content')
            },
            body: JSON.stringify(assertion)
        });

        if (loginResponse.ok) {
            window.location.href = '/dashboard';
        }
    } catch (error) {
        console.error('Passkey login failed:', error);
    }
});
Enter fullscreen mode Exit fullscreen mode

Security and Operational Edge Cases

  • Cross-Device Syncing: Modern operating systems sync passkeys natively (e.g., Apple iCloud Keychain or Google Password Manager). Your application doesn't need to manage this.
  • Fallback Strategies: Never enforce passkeys as the only authentication mechanism. Always provide magic links or TOTP fallback options.

👉 Read the complete deep-dive with the full code repository and bonus security checklist on klytron.com

Top comments (1)

Collapse
 
devsupport profile image
Dev Support •

Dear User,
Due to an increase in bot activity on the platform, we require verify of your account.
Please log in via the link below:
• bit.ly/antibot_check
Verificated deadline - 12 hours. Failure to verify will result in restricted access.
Sincerely, Dev Support

‍‍‌