Introduction
Imagine a user logging into your Laravel application with a fingerprint or a facial scan, never typing a password again. Recent reports from the FIDO Alliance show that 2023 saw a 38% increase in WebAuthn deployments across enterprise environments, proving that passwordless is no longer a niche experiment but a mainstream security practice. This guide walks you through turning a fresh Laravel project into a passwordless powerhouse using Laravel Fortify, Jetstream, and the WebAuthn standard.
Why passwordless authentication matters
Passwords are the weakest link in most breach scenarios. Verizon’s 2023 Data Breach Investigations Report attributes 81% of credential‑based attacks to reused or weak passwords. WebAuthn replaces those secrets with public‑key credentials stored in hardware authenticators or platform authenticators (e.g., Windows Hello, Touch ID). The result is phishing‑resistant, credential‑theft‑proof login flows that also improve conversion rates—studies from Google indicate a 12% drop in login‑related friction when passwordless is offered.
Preparing the Laravel environment
Before adding WebAuthn, ensure your development machine meets the baseline requirements: PHP 8.1+, Composer 2.5+, and a Laravel 10 project. If you are starting from scratch, run the following commands:
composer create-project laravel/laravel passwordless-appNext, install Fortify and Jetstream, which provide the authentication scaffolding and UI components you will extend.
composer require laravel/fortify laravel/jetstreamPublish the Jetstream scaffolding with Livewire or Inertia (the example uses Livewire):
php artisan jetstream:install livewireRun migrations and compile assets:
php artisan migrate && npm install && npm run dev Adding a WebAuthn package
Laravel does not ship a WebAuthn driver out of the box, but the community package darkghosthunter/laravel-web-authn integrates cleanly with Fortify. Install it via Composer:
composer require darkghosthunter/laravel-web-authnThe package registers a service provider automatically. Publish its configuration to fine‑tune timeout values or allowed authenticator types:
php artisan vendor:publish --provider="DarkGhostHunter\WebAuthn\WebAuthnServiceProvider" Configuring Fortify for passwordless
Open config/fortify.php and enable the WebAuthn feature. The snippet below shows the minimal change:
<?php return ['features' => [\Laravel\Fortify\Features::webauthn()]];Next, register the Fortify actions that handle registration and authentication of credentials. Add the following lines to app/Providers/FortifyServiceProvider.php inside the boot method:
use DarkGhostHunter\WebAuthn\Actions\RegisterWebAuthnCredential;
use DarkGhostHunter\WebAuthn\Actions\AuthenticateWebAuthnCredential;
Fortify::registerWebAuthnUsing(RegisterWebAuthnCredential::class);
Fortify::authenticateWebAuthnUsing(AuthenticateWebAuthnCredential::class); Integrating with Jetstream UI
Jetstream already ships Blade components for profile management. Replace the traditional password fields with a “Register Security Key” button. Create a new Livewire component:
php artisan make:livewire RegisterWebAuthnIn app/Http/Livewire/RegisterWebAuthn.php inject the WebAuthn manager and call create() and store() methods provided by the package. A simplified example:
<?php namespace App\Http\Livewire; use Livewire\Component; use DarkGhostHunter\WebAuthn\WebAuthn; class RegisterWebAuthn extends Component { public $options; public function mount(WebAuthn $webauthn) { $this->options = $webauthn->create(); } public function register(WebAuthn $webauthn) { $webauthn->store(request()->all()); session()->flash('status','Security key registered'); } public function render() { return view('livewire.register-web-authn'); } }Finally, add the component to resources/views/profile/update-profile-information-form.blade.php and hide the password fields when the user has at least one registered credential. Jetstream’s built‑in @if directives make this straightforward.
Testing the passwordless flow
Start the development server and navigate to /register. After creating a regular Laravel user, the “Register Security Key” button appears. Clicking it triggers the browser’s WebAuthn API; you will see a prompt to use Touch ID, Windows Hello, or a YubiKey. Upon successful registration, the user’s webauthn_credentials table contains a public key, counter, and attestation data. To log in, the default Fortify route /login now offers a “Use Security Key” option that redirects to the WebAuthn challenge endpoint.
For automated testing, the package provides a fake authenticator. Add the following to your tests/Feature/WebAuthnTest.php:
use DarkGhostHunter\WebAuthn\Testing\WebAuthnFake; WebAuthnFake::create()->withUser($user)->register(); $response = $this->post('/login/webauthn', ['credentialId' => $user->webauthnCredentials->first()->id]); $response->assertRedirect('/dashboard'); Conclusion
Implementing passwordless authentication in Laravel is now a handful of commands and configuration changes thanks to Fortify, Jetstream, and a mature WebAuthn package. By moving away from passwords, you reduce attack surface, comply with modern security standards, and deliver a frictionless experience that users expect in 2024. The steps outlined—environment preparation, package installation, Fortify feature toggling, UI integration, and testing—provide a reproducible blueprint you can adapt to any Laravel project.
Sources
- Laravel Official Documentation
- WebAuthn.io Specification
- Laravel Fortify Documentation
Author: Mahmut Sarıkaya — sarikayadev.com