Challenge-Response über Nitro-Server-Routes und wann klassische Anmeldung noch sinnvoll bleibt
Passkeys ersetzen das klassische Passwort durch ein kryptografisches Schlüsselpaar, das der Browser über die WebAuthn-API verwaltet. In diesem Artikel zeigen wir, wie eine Nuxt-Anwendung die Registrierung und Anmeldung per Passkey umsetzt, welche Rolle die Nitro-Server-Routes bei der Challenge-Generierung spielen und warum ein Fallback für ältere Browser weiterhin notwendig ist.
Inhaltsverzeichnis
- 1. Was WebAuthn eigentlich ist
- 2. Vergleich zu klassischer Passwort-plus-2FA-Authentifizierung
- 3. Serverseitige Challenge-Generierung über Nitro
- 4. Registrierung im Client mit navigator.credentials
- 5. Anmeldung und Verifikation der Assertion
- 6. Fallback-Strategie für Browser ohne WebAuthn-Support
- 7. Usernameless Login und Conditional UI
- 8. Account-Wiederherstellung und Multi-Device-Handling
- 9. Sicherheitsaspekte und Betriebs-Checkliste
- 10. Zusammenfassung
- 11. FAQ
1. Was WebAuthn eigentlich ist
WebAuthn ist ein Standard des W3C, der Authentifizierung über asymmetrische Kryptografie statt über geteilte Geheimnisse wie Passwörter ermöglicht. Beim Registrieren erzeugt ein Authenticator, etwa der Fingerabdrucksensor eines Laptops, Windows Hello oder ein separater Sicherheitsschlüssel, ein Schlüsselpaar, dessen privater Teil den Authenticator niemals verlässt. Der Server speichert ausschließlich den öffentlichen Schlüssel und kann damit später jede Signatur prüfen, ohne selbst ein Geheimnis vorhalten zu müssen, das gestohlen werden könnte.
Der Begriff Passkey bezeichnet dabei eine bestimmte, nutzerfreundliche Ausprägung einer WebAuthn-Credential: eine sogenannte discoverable Credential, die sich der Browser merkt und die über ein Plattformkonto wie die iCloud-Schlüsselbund-Funktion oder den Google-Passwortmanager automatisch zwischen den Geräten eines Nutzers synchronisiert. Ältere, nicht-discoverable WebAuthn-Credentials blieben dagegen an ein einzelnes Gerät gebunden und mussten pro Gerät einzeln registriert werden, was für die breite Nutzung deutlich unpraktischer war.
2. Vergleich zu klassischer Passwort-plus-2FA-Authentifizierung
Klassische Anmeldung mit Passwort und zweitem Faktor bleibt trotz zusätzlichem Schutz anfällig für Phishing, denn ein Passwort lässt sich auf einer gefälschten Seite genauso eintippen wie auf der echten, und auch ein per SMS oder App generierter Einmalcode lässt sich in Echtzeit an einen Angreifer weiterleiten. WebAuthn-Signaturen sind dagegen fest an die Origin gebunden, unter der sie erzeugt wurden, sodass eine Signatur, die für mironsoft.de erstellt wurde, auf keiner anderen Domain jemals gültig ist, selbst wenn die gefälschte Seite optisch identisch aussieht.
Auch beim Bedienkomfort liegt der Vorteil meist bei Passkeys: ein einziger biometrischer Prompt oder eine kurze PIN-Eingabe ersetzt das Eintippen eines Passworts und das anschließende Abrufen eines Codes aus einer separaten App oder SMS. Der Umstieg bringt allerdings eigene UX-Herausforderungen mit sich, etwa eine klare Führung beim erstmaligen Einrichten auf einem neuen Gerät sowie einen durchdachten Wiederherstellungsweg für den Fall, dass alle registrierten Geräte gleichzeitig verloren gehen.
3. Serverseitige Challenge-Generierung über Nitro
Der Registrierungsablauf beginnt serverseitig: der Server erzeugt eine zufällige, nur einmal gültige Challenge und baut daraus zusammen mit Angaben zu Nutzer und Relying Party die publicKeyCredentialCreationOptions, die an den Client geschickt werden. Der Browser ruft damit navigator.credentials.create() auf, lässt den Nutzer den Authenticator bestätigen und schickt die entstandene Attestation-Response zurück an den Server, der sie verifiziert und bei Erfolg den öffentlichen Schlüssel zusammen mit der Credential-ID dauerhaft speichert.
In der Praxis übernimmt eine Bibliothek wie simplewebauthn diese kryptografische Detailarbeit sowohl beim Erzeugen der Optionen als auch bei der späteren Verifikation, sodass eigener Code sich vor allem um Speicherung und Anbindung an das Nutzerkonto kümmert. Der folgende Ausschnitt zeigt eine Nitro-Serverroute, die die Registrierungsoptionen erzeugt und die zugehörige Challenge kurzlebig im Nitro-Storage ablegt, damit sie beim nächsten Schritt gegengeprüft werden kann.
// server/api/webauthn/register-options.post.ts
import { generateRegistrationOptions } from '@simplewebauthn/server'
import { defineEventHandler, readBody } from 'h3'
export default defineEventHandler(async (event) => {
const { userId, username } = await readBody(event)
const options = await generateRegistrationOptions({
rpName: 'Mironsoft Shop',
rpID: 'mironsoft.de',
userID: Buffer.from(userId),
userName: username,
attestationType: 'none',
authenticatorSelection: {
residentKey: 'required',
userVerification: 'preferred'
}
})
await useStorage('webauthn-challenges').setItem(userId, options.challenge)
return options
})
4. Registrierung im Client mit navigator.credentials
Auf der Client-Seite ruft die Funktion startRegistration aus dem Paket @simplewebauthn/browser die zuvor vom Server gelieferten Optionen entgegen und kümmert sich um den korrekten Aufruf von navigator.credentials.create(), inklusive der nötigen Base64URL-Kodierung der binären Felder. Der Browser zeigt daraufhin den plattformeigenen Prompt an, etwa den Fingerabdrucksensor oder Windows Hello, und liefert bei Erfolg eine Attestation-Response zurück, die anschließend unverändert an eine zweite Serverroute geschickt wird.
Diese zweite Route ruft verifyRegistrationResponse auf, vergleicht die im Response enthaltene Challenge mit dem zuvor gespeicherten Wert, prüft Origin und rpID auf Übereinstimmung und extrahiert im Erfolgsfall den öffentlichen Schlüssel. Erst danach wird die neue Credential, bestehend aus Credential-ID, öffentlichem Schlüssel und einem Signatur-Zähler, dauerhaft in der Datenbank mit dem betreffenden Nutzerkonto verknüpft.
5. Anmeldung und Verifikation der Assertion
Beim Anmelden erzeugt der Server erneut eine frische Challenge, diesmal zusammen mit einer allowCredentials-Liste, die auf die für den Nutzer bekannten Credential-IDs verweist, oder, im usernamelosen Fall, ganz ohne diese Liste. Der Browser ruft navigator.credentials.get() auf, der Nutzer wählt seinen Passkey aus und der Authenticator signiert die Challenge mit dem zuvor niemals übertragenen privaten Schlüssel, ohne dass dieser den Authenticator jemals verlassen muss.
Serverseitig prüft verifyAuthenticationResponse die Signatur gegen den gespeicherten öffentlichen Schlüssel und vergleicht außerdem den mitgelieferten Signatur-Zähler mit dem zuletzt gespeicherten Wert, um geklonte Authenticatoren zu erkennen. Fällt die Prüfung positiv aus, stellt die Anwendung wie gewohnt eine Session aus, etwa über ein httpOnly-Cookie, und der Passkey-Login ist damit vollständig abgeschlossen.
6. Fallback-Strategie für Browser ohne WebAuthn-Support
Ob ein Browser WebAuthn überhaupt unterstützt, lässt sich einfach per Feature-Detection anhand von window.PublicKeyCredential prüfen, zusätzlich liefert PublicKeyCredential.isConditionalMediationAvailable() Auskunft darüber, ob auch die Autofill-Variante zur Verfügung steht. Fehlt eine der beiden Fähigkeiten, sollte die Anwendung ohne Umschweife auf ein klassisches Anmeldeformular zurückfallen, statt den Nutzer mit einer fehlschlagenden Passkey-Anfrage zu konfrontieren.
In der Praxis empfiehlt es sich, Passwort- oder Magic-Link-Anmeldung dauerhaft als parallele Methode anzubieten statt Passkeys zu erzwingen, denn ältere Browser, manche eingebetteten In-App-Webviews sowie stark abgesicherte Unternehmensumgebungen unterstützen WebAuthn teilweise nach wie vor nicht vollständig. Ein erzwungener Passkey-Only-Ansatz würde diese Nutzergruppe schlicht von der Anwendung aussperren.
7. Usernameless Login und Conditional UI
Weil discoverable Credentials direkt im Browser oder in der Systemschlüsselverwaltung hinterlegt sind, erlaubt WebAuthn eine Anmeldung, ohne vorher einen Nutzernamen einzutippen: der Browser zeigt stattdessen eine Auswahl der für die aktuelle Relying Party gespeicherten Passkeys an, aus der der Nutzer den passenden auswählt. Diese sogenannte Usernameless- oder Discoverable-Login-Variante reduziert die Anzahl der nötigen Interaktionsschritte weiter.
Die Conditional-UI-Erweiterung geht noch einen Schritt weiter und blendet passende Passkeys bereits als Autofill-Vorschlag direkt im gewöhnlichen Username-Eingabefeld ein. Technisch wird dazu navigator.credentials.get() bereits beim Laden der Seite mit der Option mediation: 'conditional' aufgerufen, ohne dass der Nutzer vorher eine Aktion auslösen muss, der Browser wartet dann einfach im Hintergrund auf eine Auswahl.
8. Account-Wiederherstellung und Multi-Device-Handling
Jede Anwendung mit Passkey-Login braucht einen Weg für den Fall, dass ein Nutzer sämtliche registrierten Geräte gleichzeitig verliert, etwa eine klassische E-Mail-Verifikation, Backup-Codes oder einen manuellen Support-Prozess. Ebenso wichtig ist eine Verwaltungsoberfläche, über die Nutzer mehrere Passkeys für unterschiedliche Geräte registrieren und einzelne davon bei Bedarf wieder entfernen können.
Plattform-Passkeys synchronisieren innerhalb eines Ökosystems meist automatisch, etwa über die iCloud-Schlüsselbund-Funktion zwischen allen Apple-Geräten eines Kontos oder über den Google-Passwortmanager zwischen Android-Geräten. Der Wechsel zwischen unterschiedlichen Ökosystemen, etwa von einem Android-Handy zu einem Windows-Rechner, läuft dagegen über den sogenannten Hybrid-Transport per QR-Code. Für jede registrierte Credential sollte die Datenbank zusätzlich Metadaten wie Gerätename und Erstellungsdatum speichern, damit die Verwaltungsoberfläche verständlich bleibt.
9. Sicherheitsaspekte und Betriebs-Checkliste
Die rpID muss exakt der Domain entsprechen, unter der die Anwendung ausgeliefert wird, andernfalls schlägt bereits die Prüfung im Browser fehl, bevor überhaupt eine Anfrage den Server erreicht. Ebenso zwingend ist die serverseitige Prüfung der Origin bei jeder Verifikation, außerdem sollten gespeicherte Challenges nur wenige Minuten gültig und nach der Nutzung sofort wieder gelöscht werden, um Replay-Angriffe auszuschließen.
Zusätzlich empfiehlt sich Rate-Limiting für Registrierungs- und Anmelde-Endpunkte, ein waches Auge auf Anomalien beim Signatur-Zähler sowie regelmäßige Updates der eingesetzten WebAuthn-Bibliothek, da sich Details der Spezifikation und der Browser-Implementierungen weiterentwickeln. Vor jedem Rollout lohnt sich außerdem ein Test auf den wichtigsten Plattformen, also Chrome auf Android, Safari auf iOS sowie Windows Hello, da sich kleinere Verhaltensunterschiede zwischen den Implementierungen durchaus zeigen können.
| Aspekt | Passwort + 2FA | Passkey (WebAuthn) |
|---|---|---|
| Phishing-Resistenz | Passwort abtippbar, OTP abfangbar | An Origin gebunden, praktisch phishing-resistent |
| Nutzer-Aufwand | Passwort merken plus Code abrufen | Ein Biometrie-Prompt oder eine PIN |
| Serverseitige Speicherung | Passwort-Hash plus Secret für 2FA | Nur öffentlicher Schlüssel, kein Geheimnis |
| Geräteverlust | Reset per E-Mail meist ausreichend | Weiteres registriertes Gerät oder Recovery-Weg nötig |
| Browser-Unterstützung | Universell | Moderne Browser, Fallback weiterhin nötig |
Mironsoft
Vue-Architektur, Composition API und Nuxt-Performance
Vue-Anwendungen, die mit jedem Feature nicht komplizierter werden?
Wir prüfen bestehende Vue- und Nuxt-Projekte auf unstrukturierte Composables, ungenutzte Reaktivität und aufgeblähte Bundles und bauen daraus eine Architektur, die neue Features aufnimmt, ohne die Codebasis unübersichtlicher zu machen.
Architektur-Review
Composables, State-Management und Komponentenstruktur auf Wartbarkeit prüfen.
Performance-Audit
Reaktivitäts-Overhead, Bundle-Größe und Nuxt-Rendering-Strategie systematisch optimieren.
Nuxt-Integration
SSR/SSG-Setup und API-Anbindung robust und typsicher aufbauen.
10. Zusammenfassung
Passkeys und WebAuthn in Nuxt: Das Wichtigste auf einen Blick
Standard
WebAuthn (W3C), Passkey als nutzerfreundliches Branding einer discoverable Credential.
Serverseitig
Challenge-Generierung und Verifikation über Nitro-Server-Routes.
Sicherheit
An die Origin gebunden, dadurch praktisch phishing-resistent.
Fallback
Passwort- oder Magic-Link-Anmeldung für nicht unterstützte Umgebungen.