Passkeys / WebAuthn
WebAuthn (Web Authentication, Teil des FIDO2-Standards) ermöglicht passwortlose und Zwei-Faktor-Authentifizierung mit Gerätebiometrie (Touch ID, Face ID, Windows Hello), in das Betriebssystem integrierten Plattform-Authentifikatoren oder mobilen Hardware-Sicherheitsschlüsseln (YubiKey, Titan Key).
Auris implementiert die WebAuthn-API sowohl als eigenständige passwortlose Authentifizierungsmethode als auch als 2FA-Zweifaktor nach E-Mail/Passwort-Login.
Browser-Unterstützung
WebAuthn wird in allen modernen Browsern unterstützt:
| Browser | Version | Plattform-Authentifikator | Roaming-Schlüssel |
|---|---|---|---|
| Chrome | 67+ | Ja (Windows Hello, Touch ID) | Ja (USB, NFC, BLE) |
| Safari | 14+ | Ja (Touch ID, Face ID) | Ja |
| Firefox | 60+ | Eingeschränkt | Ja (USB) |
| Edge | 18+ | Ja (Windows Hello) | Ja |
| Safari (iOS) | 14+ | Ja (Face ID, Touch ID) | Ja |
| Chrome (Android) | 70+ | Ja (Fingerabdruck) | Ja (NFC) |
Passkeys (synchronisierte Anmeldedaten über iCloud Keychain, Google Password Manager oder 1Password) sind eine Form von WebAuthn. Auris unterstützt Passkey-Registrierung und -Authentifizierung, wenn der Authentifikator des Geräts die residentKey-Anforderung unterstützt.
Funktionsweise
Registrierungs-Flow
Server generiert eine Registrierungs-Challenge
Auris generiert eine kryptografisch zufällige Challenge und gibt diese zusammen mit Registrierungsoptionen (Relying-Party-Infos, Benutzerinfos, erlaubte Credential-Typen, Authentifikator-Auswahlkriterien) zurück.
Browser erstellt ein Credential
Der Browser ruft navigator.credentials.create() mit den Registrierungsoptionen auf. Der Plattform-Authentifikator (oder Sicherheitsschlüssel) fordert den Benutzer zur biometrischen Bestätigung oder PIN-Eingabe auf, generiert ein öffentlich/privates Schlüsselpaar und gibt den öffentlichen Schlüssel und ein Attestierungsobjekt zurück.
Server speichert das Credential
Deine Anwendung sendet die Credential-Daten an Auris. Auris verifiziert die Attestierung, extrahiert den öffentlichen Schlüssel und speichert die Credential-ID und den öffentlichen Schlüssel in der Datenbank, verknüpft mit dem Konto des Benutzers.
Authentifizierungs-Flow
Server generiert eine Authentifizierungs-Challenge
Auris generiert eine neue Challenge und gibt Authentifizierungsoptionen zurück, einschließlich der Liste der für den Benutzer registrierten Credential-IDs (oder leer für den Discoverable/Resident-Key-Flow).
Browser führt eine Assertion durch
Der Browser ruft navigator.credentials.get() auf. Der Authentifikator findet ein passendes Credential, signiert die Challenge mit dem privaten Schlüssel und gibt die Assertion zurück.
Server verifiziert die Assertion
Auris verifiziert die Assertion-Signatur mit dem gespeicherten öffentlichen Schlüssel. Bei Gültigkeit ist die Authentifizierung abgeschlossen und Tokens werden ausgestellt.
Client-seitige Bibliothek
Auris verwendet @simplewebauthn/browser auf der Client-Seite, um die Browser-WebAuthn-API-Aufrufe zu verwalten. Diese Bibliothek abstrahiert Browser-Kompatibilitätsprobleme und bietet typisierte Wrapper um navigator.credentials.create() und navigator.credentials.get().
npm install @simplewebauthn/browserImplementierung
Passkey registrieren
JavaScript
import { startRegistration } from '@simplewebauthn/browser'
async function registerPasskey(accessToken) {
// Schritt 1: Registrierungsoptionen von Auris abrufen
const optionsResponse = await fetch('/api/user/2fa/webauthn/challenge', {
method: 'POST',
headers: {
Authorization: `Bearer ${accessToken}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ type: 'registration' }),
})
const options = await optionsResponse.json()
// Schritt 2: Browser fordert Benutzer zur Biometrie/Sicherheitsschlüssel auf
let credential
try {
credential = await startRegistration(options)
} catch (err) {
if (err.name === 'NotAllowedError') {
console.error('Benutzer hat abgebrochen oder Zeitüberschreitung')
return
}
throw err
}
// Schritt 3: Credential an Auris senden
const verifyResponse = await fetch('/api/user/2fa/webauthn', {
method: 'POST',
headers: {
Authorization: `Bearer ${accessToken}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
credential,
name: 'Mein MacBook', // Vom Benutzer angegebener Name für das Credential
}),
})
const result = await verifyResponse.json()
if (result.verified) {
console.log('Passkey erfolgreich registriert')
}
}Mit Passkey authentifizieren
import { startAuthentication } from '@simplewebauthn/browser'
async function authenticateWithPasskey() {
// Schritt 1: Authentifizierungsoptionen von Auris abrufen
const optionsResponse = await fetch('/api/auth/webauthn/challenge', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
})
const options = await optionsResponse.json()
// Schritt 2: Browser fordert Benutzer auf (Biometrie oder Key-Tipp)
let assertion
try {
assertion = await startAuthentication(options)
} catch (err) {
if (err.name === 'NotAllowedError') {
console.error('Benutzer hat abgebrochen oder kein passendes Credential gefunden')
return
}
throw err
}
// Schritt 3: Assertion verifizieren und Tokens empfangen
const verifyResponse = await fetch('/api/auth/webauthn/verify', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ assertion }),
})
const result = await verifyResponse.json()
if (result.accessToken) {
// Tokens speichern und weiterleiten
console.log('Authentifiziert als:', result.user)
}
}WebAuthn als 2FA
Wenn als zweiter Faktor (statt als eigenständige passwortlose Methode) konfiguriert, wird WebAuthn nach dem ersten Faktor (E-Mail/Passwort oder Social) verwendet.
Konfiguration: WebAuthn-2FA in Console → Authentication → Multi-Factor Authentication → Passkeys aktivieren.
2FA-Flow:
- Benutzer schließt Erstauthentifizierung ab
- Auris stellt ein Partial-Session-Token aus (kein vollständiges Access Token)
- Deine Anwendung verwendet das Partial-Token, um den WebAuthn-Challenge-Endpunkt aufzurufen
- Benutzer tippt auf seinen Sicherheitsschlüssel oder scannt Biometrie
- Assertion wird verifiziert; Auris stellt vollständige Access- und Refresh-Tokens aus
/api/user/2fa/webauthnRegistriert ein neues WebAuthn-Credential für den authentifizierten Benutzer. Erfordert eine gültige Registrierungs-Challenge vom Challenge-Endpunkt.
/api/user/2fa/webauthn/challengeGeneriert eine WebAuthn-Registrierungs- oder Authentifizierungs-Challenge. Body: { type: 'registration' | 'authentication' }.
/api/user/2fa/webauthn/:credentialIdEntfernt ein registriertes WebAuthn-Credential anhand seiner ID.
Credential-Verwaltung
Benutzer können mehrere Credentials registrieren — eines pro Gerät oder Sicherheitsschlüssel. Jedem Credential kann ein sprechender Name zur Identifikation gegeben werden.
Best Practice ist es, Benutzern die Registrierung von mindestens zwei Credentials zu ermöglichen (z. B. Touch ID eines Laptops und ein Hardware-Schlüssel), damit sie ein Backup haben, wenn ein Gerät verloren geht.
Auris speichert pro Credential:
- Credential-ID (Base64url-kodiert, eindeutige Kennung)
- Öffentlicher Schlüssel (COSE-Format)
- Signatur-Zähler (wird bei jeder Verwendung erhöht; Auris erkennt Authentifikator-Klonen, wenn der Zähler zurückgeht)
- Vom Benutzer angegebener Name
- Zeitstempel der letzten Verwendung
- Erstellungszeitstempel
- Authentifikator-Anhangtyp (Plattform oder Cross-Plattform)
Sicherheitsüberlegungen
Phishing-Resistenz — WebAuthn-Credentials sind an den Relying-Party-Ursprung (rpId, also deine Domain) gebunden. Eine Phishing-Site auf einer anderen Domain kann keine für deine Domain ausgestellten Credentials verwenden. Dies ist ein erheblicher Vorteil gegenüber Passwörtern und OTP-Codes.
Keine gemeinsamen Geheimnisse — Der private Schlüssel verlässt niemals den Authentifikator. Auris speichert nur den öffentlichen Schlüssel. Ein Datenbankbruch legt kein Credential-Material frei.
Signatur-Zähler-Validierung — Auris verfolgt den internen Signaturzähler des Authentifikators. Wenn eine Assertion mit einem Zähler gleich oder kleiner dem gespeicherten Wert eintrifft, markiert Auris das Credential als potenziell geklont und erfordert eine Überprüfung.
Resident Keys und auffindbare Credentials — Wenn residentKey: required gesetzt ist, wird das Credential auf dem Authentifikator selbst gespeichert und kann ohne vorherige Angabe eines Benutzernamens verwendet werden (Benutzername-loser Login). Dies ist die Grundlage moderner Passkey-Flows.
Für maximale Sicherheit kombiniere WebAuthn mit einem Plattform-Authentifikator (in das Gerät integriert) statt nur einem Roaming-Sicherheitsschlüssel. Plattform-Authentifikatoren erfordern das Entsperren des Geräts (Biometrie oder PIN), was einen Besitz- und Wissens-Faktor ohne Benutzerreibung hinzufügt.
Verwandte Anleitungen
- SMS OTP — Telefonbasierter zweiter Faktor
- Hosted Login (PKCE) — Standard-interaktiver Authentifizierungs-Flow
- Magic Links — E-Mail-basierte passwortlose Authentifizierung