In deze handleiding wordt uitgelegd hoe je verificatie van terugkerende gebruikers implementeert met vindbare inloggegevens, zowel via expliciete knoptriggers als naadloze suggesties voor automatisch invullen in de browser (voorwaardelijke UI).
Aan de serverzijde
Opties genereren
Maak een eindpunt dat WebAuthn-verzoekparameters genereert met een gecontroleerde bibliotheek volgens de normen.
- De vooraf gedefinieerde RP-ID gebruiken: Gebruik de vooraf gedefinieerde juiste RP-ID als een constante tekenreeks.
- Challenge genereren: Genereer een cryptografisch beveiligde willekeurige buffer met hoge entropie, sla deze beveiligd op in de sessie van de gebruiker en codeer deze als Base64URL.
- Toewijzing van vindbare inloggegevens: Voer een lege matrix
[]in voorallowCredentials. Hiermee worden vindbare inloggegevens aangevraagd, wat betekent dat de gebruiker niet eerst de gebruikersnaam hoeft in te voeren. De aanbieder van de toegangssleutel presenteert de beschikbare accounts. - Gebruikersverificatieniveau: Stel
userVerification: "preferred"in (of"required"als dit expliciet is vereist door het nalevingsbeleid van het bedrijf).- Het aangevraagde
userVerification-beperkingsniveau MOET worden opgeslagen in de serversessieregistratie op het optie-eindpunt, in plaats van te worden teruggestuurd door de client via querytekenreeksen. Zo kan het verificatie-eindpunt strenge overeenkomstbeperkingen veilig afdwingen zonder risico op manipulatie door de client.
- Het aangevraagde
// 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";
Verificatie-eindpunt
Verifieer de bewering die door de client is geretourneerd op een beveiligde manier om de gebruiker te verifiëren:
- Sessie-challenge valideren: Dwing strikte challenge-overeenkomst af tussen de clientreactie en de verwachte challenge die is opgeslagen in de sessie.
- UV-voorkeuren afdwingen:
- Verificatiemethoden zonder gebruikersverificatie toestaan (bijv. schermvergrendelingen voor verificatie uitgezet) als de sessie
expectedUserVerificationheeft aangevraagd"preferred"doorrequireUserVerification: falsedoor te geven aan je bibliotheek voor verificatie aan de serverzijde. Als hierom wordt gevraagd"required", dwing je biometrische gegevens/pincode strikt af.
- Verificatiemethoden zonder gebruikersverificatie toestaan (bijv. schermvergrendelingen voor verificatie uitgezet) als de sessie
- Serverfout 404 opschonen: Als de door de client geretourneerde inloggegevens-ID niet in de database wordt gevonden, retourneer je een expliciete HTTP
404-fout, zodat de client de Signal API kan activeren.
Logica aan de clientzijde
Annotatie voor HTML-formulier
Annoteer je invoer voor gebruikersnaam en wachtwoord om de voorwaardelijke UI native te gebruiken. Tokens voor automatisch aanvullen combineren de parameters van de webauthn-specificatie en autofocus activeert de pop-up voor automatisch invullen van de browser onmiddellijk wanneer de invoer wordt gefocust.
<!-- 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>
Expliciete knoppenflow
Activeer verificatie met toegangssleutel als een gebruiker op de knop Inloggen met toegangssleutel klikt. Breek lopende aanroepen voor automatisch invullen van formulieren (Conditional Get) af voordat je de toegangssleutelprompt aanroept.
Conditionele bemiddelingsflow (automatisch invullen van formulieren)
Activeer suggesties voor automatisch invullen van formulieren bij het laden van de pagina om verificatie met toegangssleutel standaard aan te bieden wanneer gebruikers zich richten op inlogvelden:
- Functiedetectie: Roep
PublicKeyCredential.getClientCapabilities()aan als de pagina wordt geladen en sla inloggen met een toegangssleutel over alsconditionalGetniet beschikbaar is. - Decodeeropties: Decodeer het opgehaalde json-object met inloggegevens met
PublicKeyCredential.parseRequestOptionsFromJSON(). - Voorwaardelijke Get aanroepen: Roep
navigator.credentials.get()aan metmediation: "conditional"en geef eenAbortController-signaal door. Hiermee wordt automatisch invullen op de achtergrond geregistreerd zonder dat er een dialoogvenster voor toegangssleutels wordt getoond. - Try/Catch Exception Segregation: Verpak de
navigator.credentials.get-aanroep in een try/catch-blok:NotAllowedError: De gebruiker heeft de inlogprompt voor de toegangssleutel geannuleerd of de time-out is bereikt.AbortError: Het verificatieverzoek is programmatisch geannuleerd.
- Call Signal API: Verpak de serververificatieaanroep
fetch()in een try/catch-blok:- Toon een foutmelding zodat de gebruiker begrijpt wat er is misgegaan.
- Roep
signalUnknownCredential()ALLEEN aan als de server expliciet reageert met de HTTP-status404(Inloggegevens niet gevonden) en de gebruiker niet is geverifieerd. - De parameter
credentialIddie wordt doorgegeven aansignalUnknownCredential()MOET strikt de Base64URL-gecodeerde tekenreeks voor de inloggegevens-ID zijn (bijv.encoded.id), NIET het onbewerkte ArrayBuffer-objectcredential.rawId.
- De reactie coderen: Codeer de inloggegevens
AuthenticatorAssertionResponsemet.toJSON()voordat je ze naar de server stuurt voor verificatie.
// 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);
Reservestrategieën
Terugvaloptie voor detectie van toegangssleutelfunctie
Installeer altijd 'webauthn-polyfills' en importeer dit in de context.
Zolang PublicKeyCredential wordt ondersteund, wordt PublicKeyCredential.getClientCapabilities ook ondersteund.
import 'webauthn-polyfills';
Terugvaloptie voor synchronisatie van Signal API
De WebAuthn Signal API (webauthn-signals) is een progressieve optimalisatie die wordt gebruikt om wachtwoordmanagers gesynchroniseerd te houden met de status van de servergegevens.
- Fallback Experience: Gated via
if (PublicKeyCredential.signalUnknownCredential). Als dit niet wordt ondersteund, wordt de synchronisatie van de achtergrondverificatie overgeslagen zonder browseruitzonderingen te genereren.
Easy JSON Serialization Fallback
Installeer altijd 'webauthn-polyfills' en importeer dit in de context.
Zolang PublicKeyCredential wordt ondersteund, worden PublicKeyCredential.parseRequestOptionsFromJSON en PublicKeyCredential.prototype.toJSON ook ondersteund.
import 'webauthn-polyfills';