Skip to Content

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:

BrowserVersionPlattform-AuthentifikatorRoaming-Schlüssel
Chrome67+Ja (Windows Hello, Touch ID)Ja (USB, NFC, BLE)
Safari14+Ja (Touch ID, Face ID)Ja
Firefox60+EingeschränktJa (USB)
Edge18+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/browser

Implementierung

Passkey registrieren

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:

  1. Benutzer schließt Erstauthentifizierung ab
  2. Auris stellt ein Partial-Session-Token aus (kein vollständiges Access Token)
  3. Deine Anwendung verwendet das Partial-Token, um den WebAuthn-Challenge-Endpunkt aufzurufen
  4. Benutzer tippt auf seinen Sicherheitsschlüssel oder scannt Biometrie
  5. Assertion wird verifiziert; Auris stellt vollständige Access- und Refresh-Tokens aus
POST/api/user/2fa/webauthn

Registriert ein neues WebAuthn-Credential für den authentifizierten Benutzer. Erfordert eine gültige Registrierungs-Challenge vom Challenge-Endpunkt.

POST/api/user/2fa/webauthn/challenge

Generiert eine WebAuthn-Registrierungs- oder Authentifizierungs-Challenge. Body: { type: 'registration' | 'authentication' }.

DELETE/api/user/2fa/webauthn/:credentialId

Entfernt 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