In diesem Leitfaden wird beschrieben, wie Sie die Authentifizierung wiederkehrender Nutzer mit auffindbaren Anmeldedaten implementieren, sowohl über explizite Schaltflächen-Trigger als auch über nahtlose Vorschläge für die automatische Vervollständigung im Browser (Conditional UI).
Serverseitig
Optionen generieren
Erstellen Sie einen Endpunkt, der WebAuthn-Anfrageparameter mithilfe einer geprüften Bibliothek gemäß den Standards generiert.
- Vordefinierte RP-ID verwenden: Verwenden Sie die vordefinierte korrekte RP-ID als konstanten String.
- Challenge generieren: Generieren Sie einen kryptografisch sicheren Zufallspuffer mit hoher Entropie, speichern Sie ihn sicher in der Nutzersitzung und codieren Sie ihn als Base64URL.
- Zuordnung von auffindbaren Anmeldedaten: Geben Sie ein leeres Array
[]fürallowCredentialsan. Dadurch werden auffindbare Anmeldedaten angefordert. Der Nutzer muss also nicht zuerst seinen Nutzernamen eingeben, sondern der Passkey-Anbieter zeigt die verfügbaren Konten an. - Nutzerbestätigungsstufe: Legen Sie
userVerification: "preferred"fest (oder"required", wenn dies ausdrücklich durch die Compliance-Richtlinien des Unternehmens vorgeschrieben ist).- Die angeforderte
userVerification-Einschränkungsebene MUSS im Server-Sitzungsdatensatz am Optionsendpunkt beibehalten werden, anstatt vom Client über Abfragestrings zurückgegeben zu werden. So kann der Bestätigungsendpunkt strenge Abgleichseinschränkungen sicher erzwingen, ohne dass die Gefahr einer Clientmanipulation besteht.
- Die angeforderte
// Options generation example (discoverable flow)
const options = {
challenge: serverGeneratedBase64UrlChallenge, // High-entropy random challenge stored in session
rpId: "example.com",
allowCredentials: [], // Request discoverable passkeys
userVerification: "preferred",
};
// Persist expected UV level to user session
req.session.expectedUserVerification = "preferred";
Bestätigungsendpunkt
Prüfen Sie die vom Client zurückgegebene Assertion, um den Nutzer zu authentifizieren:
- Sitzungs-Challenge validieren: Erzwingt eine strikte Übereinstimmung zwischen der Clientantwort und der erwarteten Challenge, die in der Sitzung gespeichert ist.
- UV-Einstellungen erzwingen:
- Authentifikatoren ohne Nutzerbestätigung zulassen (z.B. Displaysperren für Authentifikatoren deaktiviert), wenn die
expectedUserVerificationder Sitzung"preferred"angefordert hat, indemrequireUserVerification: falsean die serverseitige Überprüfungsbibliothek übergeben wird. Wenn"required"angefordert wird, muss die Eingabe biometrischer Daten oder der PIN erzwungen werden.
- Authentifikatoren ohne Nutzerbestätigung zulassen (z.B. Displaysperren für Authentifikatoren deaktiviert), wenn die
- Clean Serverfehler 404: Wenn die vom Client zurückgegebene Anmeldedaten-ID nicht in der Datenbank gefunden wird, geben Sie einen expliziten HTTP-Fehler
404zurück, damit der Client die Signal API auslösen kann.
Clientseitige Logik
HTML-Formularannotation
Annotieren Sie die Eingabefelder für Nutzername und Passwort, um die bedingte Benutzeroberfläche nativ zu nutzen. Autocomplete-Tokens kombinieren die Parameter der WebAuthn-Spezifikation und Autofokus löst das Browser-Autofill-Pop-up sofort aus, wenn die Eingabe fokussiert wird.
<!-- Autocomplete tokens must contain webauthn space-separated -->
<form id="signin-form">
<input
type="text"
name="username"
autocomplete="username webauthn"
autofocus
data-testid="username-field"
/>
<input type="password" name="password" autocomplete="current-password" />
<button type="submit">Sign in</button>
</form>
Expliziter Button-Ablauf
Passkey-Authentifizierung auslösen, wenn ein Nutzer auf die Schaltfläche „Mit Passkey anmelden“ klickt. Brechen Sie alle laufenden Aufrufe zum automatischen Ausfüllen von Formularen (Conditional Get) ab, bevor Sie die Passkey-Aufforderung aufrufen.
Bedingter Vermittlungsablauf (Autofill-Formular)
Aktivieren Sie beim Seitenaufbau Vorschläge für das automatische Ausfüllen von Formularen, um die Passkey-Authentifizierung nativ anzubieten, wenn Nutzer auf Anmeldefelder klicken:
- Funktionserkennung: Rufen Sie beim Laden der Seite
PublicKeyCredential.getClientCapabilities()auf und überspringen Sie die Anmeldung mit Passkey, wennconditionalGetnicht verfügbar ist. - Decodierungsoptionen: Decodieren Sie das abgerufene JSON-Objekt mit den Anmeldedaten mit
PublicKeyCredential.parseRequestOptionsFromJSON(). - Bedingten GET-Aufruf ausführen: Rufen Sie
navigator.credentials.get()mitmediation: "conditional"auf und übergeben Sie einAbortController-Signal. Dadurch wird Autofill im Hintergrund registriert, ohne dass ein Passkey-Dialogfeld gerendert wird. - Try/Catch-Ausnahmetrennung: Schließen Sie den
navigator.credentials.get-Aufruf in einen Try/Catch-Block ein:NotAllowedError: Der Nutzer hat die Passkey-Anmeldeaufforderung abgebrochen oder die Zeitüberschreitung ist abgelaufen.AbortError: Die Authentifizierungsanfrage wurde programmatisch abgebrochen.
- Signal API aufrufen: Verpacken Sie den
fetch()-Aufruf zur Serverbestätigung in einen try/catch-Block:- Zeigen Sie eine Fehlermeldung an, damit der Nutzer versteht, was schiefgelaufen ist.
- Rufen Sie
signalUnknownCredential()NUR auf, wenn der Server explizit mit dem HTTP-Status404(Anmeldedaten nicht gefunden) antwortet und der Nutzer nicht authentifiziert ist. - Der an
signalUnknownCredential()übergebene ParametercredentialIdMUSS genau der Base64URL-codierte String der Anmeldedaten-ID sein (z.B.encoded.id), NICHT das rohe ArrayBuffer-Objektcredential.rawId.
- Antwort codieren: Codieren Sie die Anmeldedaten
AuthenticatorAssertionResponsemit.toJSON(), bevor Sie sie zur Überprüfung an den Server senden.
// optionsFetch and loginVerifyFetch are app-defined HTTP methods
import { optionsFetch, loginVerifyFetch } from "./api.js";
let autofillAbortController = new AbortController();
async function initializeConditionalAutofill() {
// Feature detect Conditional Get autofill support
const capabilities = await PublicKeyCredential.getClientCapabilities();
if (capabilities.conditionalGet === true) {
const loginOptionsJSON = await optionsFetch();
const publicKey =
PublicKeyCredential.parseRequestOptionsFromJSON(loginOptionsJSON);
try {
// Initiate Conditional UI form autofill suggestions
const credential = await navigator.credentials.get({
publicKey,
signal: autofillAbortController.signal,
mediation: "conditional",
});
// Segregated verification fetch
const encoded = credential.toJSON();
const response = await loginVerifyFetch(encoded);
if (!response.ok && response.status === 404) {
// Note: this code path runs pre-authentication, satisfying the unauth precondition
if (PublicKeyCredential.signalUnknownCredential) {
await PublicKeyCredential.signalUnknownCredential({
rpId, // RP ID must match the one defined on the server
credentialId: encoded.id,
});
}
}
} catch (err) {
// Silently swallow expected client WebAuthn exceptions
if (["NotAllowedError", "AbortError"].includes(err.name)) {
return;
}
console.error("Unexpected conditional get error:", err);
}
}
}
async function triggerButtonAuthentication() {
// Abort any pending Conditional Get call to prevent passkey prompt collisions
autofillAbortController.abort();
autofillAbortController = new AbortController(); // Reset controller for next triggers
const loginOptionsJSON = await optionsFetch();
const publicKey =
PublicKeyCredential.parseRequestOptionsFromJSON(loginOptionsJSON);
let credential;
try {
// Passkey explicit prompt trigger
credential = await navigator.credentials.get({
publicKey,
signal: autofillAbortController.signal,
});
} catch (err) {
if (err.name === "NotAllowedError") {
console.log("User cancelled passkey login.");
} else if (err.name === "AbortError") {
console.log("The authentication operation was aborted.");
}
// Re-arm Conditional autofill Suggestions after cancelled explicit button prompts
initializeConditionalAutofill();
return; // Safe exit
}
// Segregated verification try/catch (HTTP 404 trigger)
const encoded = credential.toJSON();
try {
const response = await loginVerifyFetch(encoded);
if (!response.ok && response.status === 404) {
// Note: this code path runs pre-authentication, satisfying the unauth precondition
await PublicKeyCredential.signalUnknownCredential({
rpId, // RP ID must match the one defined on the server
credentialId: encoded.id, // Base64URL-encoded credential ID
});
}
} catch (serverErr) {
console.error("Verification request error:", serverErr);
}
}
// Trigger Conditional Get on load
window.addEventListener("DOMContentLoaded", initializeConditionalAutofill);
Fallback-Strategien
Fallback für die Passkey-Funktionserkennung
Installieren Sie immer „webauthn-polyfills“ und importieren Sie es in den Kontext.
Solange PublicKeyCredential unterstützt wird, wird auch PublicKeyCredential.getClientCapabilities unterstützt.
import 'webauthn-polyfills';
Signal API-Synchronisierungs-Fallback
Die WebAuthn Signal API (webauthn-signals) ist eine progressive Optimierung, mit der Passwortmanager mit dem Status der Serveranmeldedaten synchronisiert werden.
- Fallback-Erfahrung: Über
if (PublicKeyCredential.signalUnknownCredential)eingeschränkt. Wenn sie nicht unterstützt wird, wird die Synchronisierung der Hintergrundüberprüfung ohne Browserausnahmen umgangen.
Einfacher Fallback für die JSON-Serialisierung
Installieren Sie immer „webauthn-polyfills“ und importieren Sie es in den Kontext.
Solange PublicKeyCredential unterstützt wird, werden auch PublicKeyCredential.parseRequestOptionsFromJSON und PublicKeyCredential.prototype.toJSON unterstützt.
import 'webauthn-polyfills';