Zum Hauptinhalt springen
tsecurity.de LIVE
Echtzeit-Radar & Feeds
Alle RSS Feeds
👥 Community & Social
YouTube Security VideosGoogle Cloud Tech: Gemini is coming to your city(24.09.2026 um 15:00 Uhr)
AI & KI NachrichtenGoogle’s latest moonshot to put machine learning in space(24.09.2026 um 15:12 Uhr)
Windows Tipps & SecurityPoll: What's your favorite Surface of 2026?(24.09.2026 um 14:58 Uhr)
Sichere ProgrammierungStreaming Materialized Views for Live Read Models (2026)(24.09.2026 um 15:02 Uhr)
Sichere ProgrammierungA Day Is Not 86400 Seconds: The DST Bug in Your Date Math(24.09.2026 um 15:02 Uhr)
Sichere ProgrammierungSetting up Traefik: reverse proxy with automatic HTTPS(24.09.2026 um 15:02 Uhr)
Sichere ProgrammierungA 200 OK response does not prove a secret leak(24.09.2026 um 15:02 Uhr)
Sichere ProgrammierungHow hot do you like it?(24.09.2026 um 15:05 Uhr)
YouTube Security VideosGoogle Cloud Tech: Gemini is coming to your city(24.09.2026 um 15:00 Uhr)
AI & KI NachrichtenGoogle’s latest moonshot to put machine learning in space(24.09.2026 um 15:12 Uhr)
Windows Tipps & SecurityPoll: What's your favorite Surface of 2026?(24.09.2026 um 14:58 Uhr)
Sichere ProgrammierungStreaming Materialized Views for Live Read Models (2026)(24.09.2026 um 15:02 Uhr)
Sichere ProgrammierungA Day Is Not 86400 Seconds: The DST Bug in Your Date Math(24.09.2026 um 15:02 Uhr)
Sichere ProgrammierungSetting up Traefik: reverse proxy with automatic HTTPS(24.09.2026 um 15:02 Uhr)
Sichere ProgrammierungA 200 OK response does not prove a secret leak(24.09.2026 um 15:02 Uhr)
Sichere ProgrammierungHow hot do you like it?(24.09.2026 um 15:05 Uhr)
Intelligence View
⚡ tsecurity.de Intelligence

Laravel Now Has Native Passkeys: A Complete Guide to laravel/passkeys

Originally published at hafiz.dev For a long time, adding passkeys to a Laravel app meant reaching for a third-party package, assembling WebAuthn ceremonies by hand, or piecing together a tutorial that assumes you already know what a…

0
↗ Quelle (dev.to)
Reagiere als Erste:r — dein Feedback zählt!

Originally published at hafiz.dev







For a long time, adding passkeys to a Laravel app meant reaching for a third-party package, assembling WebAuthn ceremonies by hand, or piecing together a tutorial that assumes you already know what a "relying party ID" is. That's done.



In late April 2026, Laravel shipped laravel/passkeys, a first-party package authored by Taylor Otwell that gives you a complete passkey story out of the box. Server package, npm client, Fortify integration. Three pieces that click together so passwordless auth is boring to wire up, which is exactly what you want from a security feature.



I covered the Spatie passkeys approach back in January, and that's still valid if you're Livewire-heavy or already have that package running. But the native package is the right call for new projects and anything using Fortify. Here's the full setup.






What Ships in laravel/passkeys



The passkey stack has three components that each handle a distinct concern.



laravel/passkeys is the server-side Composer package. It handles WebAuthn ceremonies, manages a passkeys database table, registers routes for login, confirmation, and credential management, and fires events you can hook into. If you need custom authorization logic or your own route definitions, escape hatches are built in.



@laravel/passkeys is the npm client. It handles browser-side ceremony coordination (registration and verification) and ships first-class helpers for React, Vue, and Svelte with SSR-safe hooks so client-only APIs don't fight your framework. The public API is two methods: Passkeys.register() and Passkeys.verify(). That's it.



Fortify integration wires everything together via Features::passkeys() in your app config and a passkeys section in config/fortify.php. Fortify apps get the same endpoints and the PasskeyUser and PasskeyAuthenticatable contracts without reimplementing any glue.



The package is v0.1.0 but that's not a red flag. It's already the default in Laravel's official starter kits and used by Fortify in production. The version number signals that the public API may still evolve, not that the package is unstable.






Installation



Start by pulling in the Composer package:




composer require laravel/passkeys






Publish and run the migrations to create the passkeys table:




php artisan vendor:publish --tag=passkeys-migrations
php artisan migrate






Next, add a secret to your .env for deriving stable opaque user handles. This keeps passkey associations private even if your user IDs are sequential integers:




PASSKEYS_USER_HANDLE_SECRET=your-random-secret-here






Generate a value with:




php artisan key:generate --show






Use that output as your secret. The package falls back to APP_KEY if you leave this blank, but keeping them separate is better practice. If you ever rotate your app key, users won't lose their passkeys. You can find a full reference of available artisan commands in the Laravel Artisan Commands reference.






Configuring Your User Model



Add the PasskeyUser contract and PasskeyAuthenticatable trait to your User model:




<?php

namespace App\Models;

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

class User extends Authenticatable implements PasskeyUser
{
use PasskeyAuthenticatable;

// Rest of your model...
}






The trait assumes your users table has name and email columns. Authenticators show these values in their UI during registration and account selection. displayName falls back from name to email to the auth identifier. Same for username.



If you need different display values, override the methods directly on the model:




public function getPasskeyDisplayName(): string
{
return $this->full_name ?? $this->email;
}

public function getPasskeyUsername(): string
{
return $this->email;
}






That's the only change your model needs. No extra migrations, no pivot tables. The passkeys table handles credential storage and links to your user via a standard relationship that PasskeyAuthenticatable sets up for you.






Fortify Integration



If you're using Laravel Fortify, enabling passkeys takes one line in your features array:




use Laravel\Fortify\Features;

'features' => [
Features::registration(),
Features::resetPasswords(),
Features::emailVerification(),
Features::passkeys(), // Add this
],






Fortify automatically registers the passkey routes and wires up the contracts. Nothing else changes on the server side. Your existing authorization setup with policies and gates stays untouched: passkeys only replace the authentication step, not what happens after it.






The Config File



Publish the config if you need to customize anything:




php artisan vendor:publish --tag="passkeys-config"






The defaults in config/passkeys.php are sensible:




return [
'relying_party_id' => parse_url(config('app.url'), PHP_URL_HOST),
'allowed_origins' => [config('app.url')],
'user_handle_secret' => env('PASSKEYS_USER_HANDLE_SECRET', config('app.key')),
'timeout' => 60000,
'guard' => 'web',
'middleware' => ['web'],
'management_middleware' => ['password.confirm'],
'throttle' => 'throttle:6,1',
'redirect' => '/',
];






A few worth understanding before you change anything.



relying_party_id is your domain, derived from APP_URL. Passkeys are cryptographically bound to this value. If the domain the browser accesses doesn't match, the ceremony fails. Make sure APP_URL reflects the actual domain you're serving, especially in local development.



management_middleware defaults to password.confirm, which means users must re-confirm their password before adding or revoking passkeys. Don't disable this. It's the right friction for a security-critical action. The same principle applies here as with sensitive token operations in Passport vs Sanctum.



throttle limits passkey attempts to 6 per minute. Sensible for production. Adjust it if you have unusual traffic patterns, but don't remove it entirely.






Routes the Package Registers



You don't define any routes yourself. The server package registers these automatically:




POST   /passkeys/register/options   (generate registration challenge)
POST /passkeys/register (store the new credential)
POST /passkeys/verify/options (generate authentication challenge)
POST /passkeys/verify (authenticate with passkey)
DELETE /passkeys/{passkey} (revoke a specific passkey)






If you need custom route definitions (different middleware, prefixes, or custom controllers), you can disable auto-registration in the config and define them yourself. The underlying action classes are all public and importable, so you're not losing functionality by taking manual control.






How the WebAuthn Flow Works



It helps to see the ceremony before writing the frontend code:




View the interactive diagram on hafiz.dev




Registration follows the same pattern: browser requests options, authenticator creates a key pair, public key gets stored on your server. Nothing sensitive ever leaves the device. The private key never travels over the network, which is the core security advantage over passwords. No credentials to steal from a database breach.






Frontend Integration (Vue)



Install the npm client:




npm install @laravel/passkeys
npm run build






Here's a Vue 3 component that handles both registration (authenticated users adding a passkey) and login (on the login page):




<script setup>
import { ref } from 'vue'
import { Passkeys } from '@laravel/passkeys'

const registering = ref(false)
const verifying = ref(false)
const error = ref(null)

async function registerPasskey() {
registering.value = true
error.value = null

try {
await Passkeys.register({ name: 'My Device' })
// Passkey saved, refresh the list or show a success toast
} catch (e) {
error.value = e.message
} finally {
registering.value = false
}
}

async function loginWithPasskey() {
verifying.value = true
error.value = null

try {
await Passkeys.verify()
// Redirects automatically on success
} catch (e) {
error.value = e.message
} finally {
verifying.value = false
}
}
</script>

<template>
<div class="space-y-4">
<!-- Show on profile/settings for authenticated users -->
<button @click="registerPasskey" :disabled="registering" class="btn">
{{ registering ? 'Registering...' : 'Add a Passkey' }}
</button>

<!-- Show on your login page -->
<button @click="loginWithPasskey" :disabled="verifying" class="btn">
{{ verifying ? 'Verifying...' : 'Sign in with Passkey' }}
</button>

<p v-if="error" class="text-red-500 text-sm">{{ error }}</p>
</div>
</template>






Passkeys.register() handles the full browser ceremony: it fetches the challenge from /passkeys/register/options, prompts the authenticator, and POSTs the resulting credential back to the server. Passkeys.verify() does the same for login and then redirects to the path defined in config/passkeys.php → redirect on success.



For React, the import and API are identical. The Svelte helpers follow the same pattern. The package abstracts all the @simplewebauthn/browser ceremony complexity behind a clean two-method interface, which is what you want when you're not trying to become a WebAuthn expert.






Managing Registered Passkeys



Users should be able to see and revoke their passkeys. This matters more than people expect. Users register on their laptop, their phone, and their work machine, then wonder why three entries show up. Give them the tools to clean it up.



A basic controller looks like this:




// PasskeyController.php
use Illuminate\Http\Request;
use Laravel\Passkeys\Models\Passkey;

class PasskeyController extends Controller
{
public function index(Request $request)
{
$passkeys = $request->user()->passkeys()->latest()->get();

return view('passkeys.index', compact('passkeys'));
}

public function destroy(Request $request, Passkey $passkey)
{
$this->authorize('delete', $passkey);

$passkey->delete();

return back()->with('status', 'Passkey removed.');
}
}






The passkeys() relationship is defined by the PasskeyAuthenticatable trait. Each Passkey record has a name, created_at, and last_used_at column. Surface all three in the UI so users can tell which device is which and spot ones they don't recognise.



Wire the delete action to the DELETE /passkeys/{passkey} route the package already registered. The management_middleware (password confirm by default) protects both the management view and the delete action, so users need to re-authenticate before making changes.






Comparing to spatie/laravel-passkeys



Both packages use web-auth/webauthn-lib under the hood and get you to the same outcome. The difference is approach.



laravel/passkeys (native) is first-party and stack-agnostic on the frontend. Right choice for new Laravel 11, 12, or 13 projects and anything using Fortify. If you're starting fresh, use this.



spatie/laravel-passkeys ships Livewire components out of the box. If your app is already Livewire-heavy and you have Spatie's package working, there's no reason to migrate. The earlier passkeys guide covers that setup in full.



Don't run both at the same time. They register overlapping routes and you'll get conflicts.






Things to Get Right Before You Ship



A few things that will save you a debugging session:



HTTPS is required. WebAuthn only works on secure origins. For local development, use valet secure (Valet or Herd) or configure SSL in Sail. If APP_URL uses http://, the browser refuses to run the ceremony entirely. No error message. Just silence.



Keep password auth as a fallback. Not every user is on a passkey-capable device. Passkeys should be additive. Don't remove your existing login form. Make it an option alongside the passkey button, not a replacement for it.



Account recovery needs thought. If a user loses access to all their registered devices, how do they get back in? The package doesn't solve this. Email-based recovery or admin-initiated password resets are the standard approaches. Build this flow before you go live.



Multiple passkeys per user are supported by default. Users register on multiple devices, and that's expected. Your management UI (a list with a revoke button per passkey) handles this. Show name, created_at, and last_used_at so users can make sense of what's there.



The management_middleware default is password.confirm. Users re-confirm their password before adding or revoking passkeys. Don't strip it out. It's the same security pattern you'd apply to any sensitive account action.






Local Development



One thing that trips people up: APP_URL in .env must match the domain you're actually accessing in the browser. A mismatch makes the relying party check fail, and the error can be cryptic.




APP_URL=https://myapp.test






If you're on Valet:




valet secure myapp






That's all you need. The package reads APP_URL for its relying party config automatically.






FAQ



Does this work on Laravel 11 and 12, or only 13?



The package requires illuminate/contracts: ^11.0|^12.0|^13.0, so all three versions are supported. You don't need to upgrade to Laravel 13 to use it.



Do I need Fortify to use this?



No. Fortify integration is optional. The server package works standalone: you define your own routes and handle redirects. Features::passkeys() just automates the setup if Fortify is already in your stack.



What if I'm already using spatie/laravel-passkeys?



Stay on Spatie unless you have a specific reason to switch, especially if the Livewire setup is working. If you do migrate, uninstall the Spatie package and remove its service provider first. Don't run both simultaneously.



Is v0.1.0 stable enough for production?



The package is already the default in Laravel's official starter kits and backed by Fortify. The v0.1.0 label means the public API may evolve, not that it's experimental. For new projects, use it without hesitation.



Can I use this without a JavaScript framework?



Yes. The framework-specific helpers (Vue, React, Svelte) are convenience wrappers around the same core API. If you're using Blade without a frontend framework, you can call Passkeys.register() and Passkeys.verify() from a plain <script> block after importing @laravel/passkeys.






Get It Wired Up



Native passkeys is a small, focused addition to any Laravel project. The config is sensible by default, Fortify integration is a single line, and the frontend API is two method calls. If you're starting a new Laravel project today and want passwordless auth, this is the path.



If you're adding passkeys to an existing production app or migrating a complex auth setup, get in touch and we can work through the integration together.

SOC Incident Playbook: Vulnerability Remediation & Verification
title: Detect Exploitation - Laravel Now Has Native Passkeys: A Complete Guide to laravel/passkeys
id: f2cf696d-0f92-445d-a22b-94b81cab312f
status: experimental
description: Automatisch generierte SIEM-Erkennungsregel basierend auf CTI Intelligence
references:
  - https://tsecurity.de/
author: iShareStuff CTI Automated Detection Engine
date: 2026-09-24
logsource:
  category: network_connection
  product: any
detection:
  selection:
      CommandLine|contains:
        - 'exploit'
  condition: selection
falsepositives:
  - Legitime administrative Zugriffe oder Penetrationstests
level: high
tags:
  - attack.initial_access
rule CTI_Threat_Indicator {
    meta:
        author = "iShareStuff CTI Automated Detection Engine"
        date = "2026-09-24"
        description = "YARA Signature for "
    strings:
        $str = "Laravel Now Has Native Passkey" ascii wide
    condition:
        any of them
}
tsecurity.de Cognitive Threat RAG
Fokus-Vektor:

Kognitive Analyse für identifizierte Bedrohung: Erhöhte Bedrohungslage im Bereich Laravel Now Has Native Passkeys: A Compl.... Basierend auf 368k Vektor-Korrelationen werden sofortige Isolationsmaßnahmen für betroffene Endpunkte empfohlen.

🛡️ Angriffsfläche & Exposure

Netzwerk/Remote-Zugriff ohne Vorauthentifizierung möglich.

Empfohlene Sofortmaßnahmen
  • 1. Perimeter-Inspektion: Relevante Portfreigaben und exponierte Endpunkte unverzüglich scannen.
  • 2. Patch-Applikation: Hersteller-Hotfix einspielen oder betroffene Daemons in isolierte DMZ-Segmente überführen.
  • 3. Telemetrie & EDR-Alerts: Prozessaufrufe und Child-Processes auf anomale Shell-Spawns überwachen.
🔗 Semantisch verwandte Zero-Days MariaDB 11.7 VEC
Ähnliche Beiträge
🔍 Verwandte News

Auch interessante Nachrichten Laravel Now Has Native Passkeys: A Complete Guide to laravel/passkeys

Thematisch verwandte Begriffe: Laravel, Native, Passkeys, Complete · 6 Treffer

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Zum Aktualisieren ziehen
ZERO-DAY CVE-2026-97152 | Nanomsg versions 0.5-beta through 1.x before 1.2.3 has a remotely exploi…
Advisory →
TTS Reader • tsecurity.de Voice
tsecurity.de Icon
tsecurity.de App
Offline-Lesen, Eilmeldungen & 0ms Ladezeit

Installiere tsecurity.de direkt auf deinen Home-Bildschirm für das ultimative Vollbild-Magazinerlebnis ohne Browser-Leisten.

Nächster Beitrag
Themen-Radar & Intelligence Matrix
Echtzeit-Taxonomie nach Angriffsvektoren & Plattformen

tsecurity.de Live Threat Radar

🔴 LIVE RADAR
MONITORING
AKTIV
CVE-DATENBANK
LIVE
🔍
Community Radar & Live Chat
Sentinel Bot online • Live-Stream
Dein Cluster: Security Explorer
Match:
lädt…
Verbindung zum Community-Stream wird aufgebaut...
Bearbeitungsmodus — Senden überschreibt deine Nachricht
Community-Puls — was gerade passiert
lädt…
Aktivitäten deiner Analysten
lädt…
Neues Thema oder Eilmeldung einreichen

Reiche interessante Links, Zero-Days oder Debatten ein. Die Community entscheidet per Upvote über die Veröffentlichung.

Heiß diskutierte Einreichungen
🔖 Gespeicherte Artikel
📂 Keine gespeicherten Artikel vorhanden.
Zurück Ziehen Vor
Links: vorheriger Artikel Rechts: nächster Artikel unten: schließen
News NIS-2 Frühwarnung Tier-1 Intel TTP ⏱️ 3 Min vor 10 Min
Artikeldaten werden geladen...

Zurück: vorheriger Vor: nächster
↗ Original-Quelle
Social Reaktionen Deine Reaktion zählt
Einstufung & Relevanz-Poll 0 Stimmen
In sozialen Netzwerken teilen 1-Klick