Passkeys in Laravel 13: Passwordless Authentication with WebAuthn, Step by Step
Laravel 13, released in March 2026, ships native passkey support: your users can sign in with a fingerprint, Face ID, or a security key, no password and WebAuthn built in. This guide shows you how to set it up with Fortify from start to finish.
Why Passkeys Are Replacing Passwords
Passkeys are the natural evolution of the classic login. Instead of a secret the user memorizes and the server stores (the password), authentication relies on public-key cryptography: the user's device keeps the private key and the server only stores the public one. The result is that there is no shared secret to steal in a data breach, and sign-in becomes phishing-resistant by design.
What WebAuthn Is and How a Passkey Works
WebAuthn is the W3C standard, based on FIDO2, that lets browsers manage these credentials. A passkey is a WebAuthn credential tied to a device or a cloud password manager, unlocked with biometrics, a PIN, or a physical security key. The flow splits into two ceremonies: attestation, where the user registers the credential, and assertion, where they use it to authenticate. In both, the browser does the heavy lifting and your application only receives verifiable data.
Advantages Over Traditional Password + 2FA
Code-based 2FA (SMS or authenticator apps) reduces the risk of password theft, but it still depends on a secret that can be intercepted or phished. Passkeys remove the problem at the root: with no typeable code and no shared secret, phishing has nothing to steal, even if the user lands on a fake site. The experience improves too: signing in with a fingerprint is faster than typing a password plus a six-digit code.
Passkeys in Laravel 13: What the Framework Adds
Laravel 13, codenamed Atlas, shipped on March 17, 2026, with passkey authentication among its headline features. There is no need to set up third-party services or integrate community packages: the official support arrives built into the framework's ecosystem.
Native Support with Fortify and the laravel/passkeys Package
The implementation sits on Fortify, Laravel's official authentication package. Fortify wraps the laravel/passkeys Composer package and configures it for you; the options live in your application's config/fortify.php file. If you already use Fortify for classic login, enabling passkeys is a configuration change, not a rewrite.
Requirements: PHP 8.3 and Zero Breaking Changes from Laravel 12
Laravel 13 requires PHP 8.3 as a minimum and boasted zero breaking changes: most applications upgrade without touching application code. If you are coming from Laravel 12, upgrading is the natural first step, and the rest of this guide works as-is on version 13.
Installation and Configuration
Start by installing Fortify and publishing its configuration:
composer require laravel/fortify
php artisan vendor:publish --provider="Laravel\Fortify\FortifyServiceProvider"Then, in config/fortify.php, enable passkey support in the corresponding section and register the Fortify service provider in bootstrap/providers.php if your project does not have it. The laravel/passkeys package is installed and configured automatically through Fortify, so you do not need to publish extra migrations or controllers to get started.
Registering a Passkey: The Attestation Flow
Registration is the first of the two WebAuthn ceremonies. Your server generates a registration option with a random challenge, the browser turns it into a new credential, and the server stores it against the User model.
Generating the Registration Option Server-Side
From a protected route, you return the registration option generated by laravel/passkeys, which includes the challenge and the relying party parameters, meaning your domain. The client-side JavaScript consumes it with the standard browser API:
const options = await fetch('/passkeys/register/options', {
headers: { 'Accept': 'application/json' }
}).then(r => r.json());
const credential = await navigator.credentials.create({ publicKey: options });Storing the Credential on the User Model
The credential object returned by the browser contains the id, the public key, and the attestation data. You send it to a POST route that validates it and stores the credential linked to the authenticated user. From that point on, that device can be used to sign in without a password.
Passkey Login: The Assertion Flow
The second ceremony, assertion, is what happens every time a user signs in with their passkey. The server generates an authentication option, the browser resolves it with the device's private key, and the server verifies the signature against the stored public key.
Verifying the Credential and Signing In
On the client, the call is symmetric to registration:
const options = await fetch('/passkeys/authenticate/options', {
headers: { 'Accept': 'application/json' }
}).then(r => r.json());
const assertion = await navigator.credentials.get({ publicKey: options });The assertion is sent to the server, which checks the signature and the challenge, finds the user by credential, and opens the session. All the cryptographic exchange stays in WebAuthn's hands; your application just orchestrates the flow.
Combining Passkeys with Fortify's 2FA
Since July 2026, Laravel News documents passwordless sign-in combined with Fortify's second factor. The combination is powerful for sensitive apps: the passkey replaces the password, and Fortify's 2FA adds an extra verification layer whenever you want to require it. The result is a passwordless login that keeps additional protection where you need it.
Hands-On: A Complete Passwordless Login
The full flow on Laravel 13 boils down to four pieces: a route that serves the login view, a route that generates the registration option, a route that stores the credential, and a route that verifies the assertion and authenticates the user. The Blade view includes the two JavaScript snippets above: a register-passkey button for logged-in users and a sign-in-with-passkey button for login. With the Fortify configuration enabled and the User model holding its credentials relation, the integration is complete with no external services.
Security and Compatibility
Supported Browsers and Devices
WebAuthn is supported by all major browsers: Chrome, Safari, Edge, and Firefox on their current platforms. Passkeys work with mobile biometrics (fingerprint and Face ID), Windows Hello on desktop, the Apple and Google cloud keychains, and hardware security keys such as YubiKey. Exact compatibility depends on the user's browser and operating system, so test the flow on the ones your audience uses.
Fallbacks and Account Recovery When a Device Is Lost
A passkey cannot be recovered from the server: the private key lives on the user's device. That is why, before launch, you need a recovery plan: email verification, backup codes, or Fortify's 2FA as an alternative path. The usual approach is to offer passkeys as the primary option while keeping a classic fallback for edge cases, so no user gets locked out of their account.
Conclusion
Laravel 13 makes passkeys a realistic option for any application: native support through Fortify, zero third-party services, and a standard WebAuthn flow that the browser handles for you. Passwordless authentication is not just a security improvement, it is also an experience improvement, and with framework support the implementation cost is small. If you want to keep up with Laravel, PHP, and web development, check out the rest of the articles on the blog.