Ce guide axé sur les différences explique comment implémenter l'authentification renforcée ou la revalidation pour un utilisateur connecté avant qu'il n'effectue des modifications sensibles sur son compte (par exemple, modification de mot de passe, transfert financier).
Architecture de flux Delta
Contrairement à l'authentification standard, la réauthentification par clé d'accès limite les invites de la boîte de dialogue de clé d'accès strictement aux identifiants préenregistrés de l'utilisateur connecté afin d'éviter le mélange de comptes ou l'usurpation de clé d'accès pendant les sessions actives.
Côté serveur
Delta de génération d'options
Créez un point de terminaison qui renseigne les paramètres d'identifiants autorisés spécifiquement pour l'utilisateur actif et connu :
Limiter les identifiants : renseignez le tableau d'options allowCredentials avec des enregistrements PublicKeyCredentialDescriptor spécifiques mappant tous les ID d'identifiants enregistrés pour l'utilisateur connecté. Si vous laissez ce champ vide ou si vous l'omettez, vous revenez aux identifiants détectables, ce qui compromet la sécurité de la session.
// Node.js step-up options generation example
router.post("/api/reauth/options", enforceActiveSession, async (req, res) => {
const userPasskeys = await db.findCredentialsByUserId(req.user.id);
const options = {
challenge: serverGeneratedBase64UrlChallenge, // Random challenge stored in user session
rpId: "example.com",
// Enforce allowance strictly limited to the user's credentials list
allowCredentials: userPasskeys.map((cred) => ({
type: "public-key",
id: cred.id,
transports: cred.transports, // Speeds up resolution by indicating platform transports
})),
};
return res.json(options);
});
Delta du point de terminaison de validation
Vérifiez l'assertion renvoyée par le client :
Vérifier la propriété du compte : le point de terminaison de validation DOIT vérifier explicitement que l'ID d'identifiant authentifié renvoyé par le client correspond à un enregistrement d'identifiant stocké dont l'ID utilisateur associé correspond strictement à l'utilisateur connecté actif (storedCredential.passkeyUserId === req.user.id). Si une clé d'accès valide d'un autre utilisateur est renvoyée, l'authentification DOIT être rejetée immédiatement.
Différences de flux côté client
Les applications choisissent entre deux interfaces de réauthentification en fonction de l'UI de la transaction :
A. Flux de boutons (sans champs de saisie)
Déclenchez la réauthentification lorsqu'un utilisateur appuie sur un bouton "Valider l'identité" ou "Poursuivre la transaction".
<button id="reauth-btn" data-testid="reauth-button">Confirm Transaction</button>
let reauthAbortController = new AbortController();
async function triggerButtonReauth() {
// Abort any background suggestion flows to avoid passkey prompt collisions
reauthAbortController.abort();
reauthAbortController = new AbortController();
const optionsResponse = await fetch("/api/reauth/options", {
method: "POST",
});
const optionsJSON = await optionsResponse.json();
const publicKey =
PublicKeyCredential.parseRequestOptionsFromJSON(optionsJSON);
try {
const credential = await navigator.credentials.get({
publicKey,
signal: reauthAbortController.signal,
});
if (credential) {
const encodedCredential = credential.toJSON();
const verifyResponse = await fetch("/api/reauth/verify", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(encodedCredential),
});
if (verifyResponse.ok) {
showTransactionSuccessUI();
} else if (verifyResponse.status === 404 && PublicKeyCredential.signalUnknownCredential) {
await PublicKeyCredential.signalUnknownCredential({
rpId, // RP ID must match the one defined on the server
credentialId: encodedCredential.id
});
}
}
} catch (err) {
if (err.name === "NotAllowedError") {
console.log("User cancelled reauthentication.");
}
}
}
document
.getElementById("reauth-btn")
.addEventListener("click", triggerButtonReauth);
Stratégies de remplacement
Solution de secours pour la détection des fonctionnalités de clé d'accès
Installez toujours "webauthn-polyfills" et importez-le dans le contexte.
Tant que PublicKeyCredential est compatible, PublicKeyCredential.getClientCapabilities l'est également.
import 'webauthn-polyfills';
Easy JSON Serialization Fallback
Installez toujours "webauthn-polyfills" et importez-le dans le contexte.
Tant que PublicKeyCredential est compatible, PublicKeyCredential.parseRequestOptionsFromJSON et PublicKeyCredential.prototype.toJSON le sont également.
import 'webauthn-polyfills';