En esta guía centrada en los cambios, se detalla cómo implementar la autenticación de aumento de privilegios o la nueva verificación para un usuario que accedió a su cuenta antes de que realice cambios sensibles en ella (p.ej., actualizaciones de contraseñas o transferencias financieras).
Arquitectura de Delta Flow
A diferencia de la autenticación normal, la reautenticación con llave de acceso restringe los mensajes de diálogo de la llave de acceso estrictamente a las credenciales registradas previamente del usuario que accedió para evitar la mezcla de cuentas o la falsificación de llaves de acceso durante las sesiones activas.
Del servidor
Delta de generación de opciones
Crea un extremo que complete los parámetros de credenciales permitidas específicamente para el usuario activo y conocido:
Restringe las credenciales: Completa el array de opciones allowCredentials con registros PublicKeyCredentialDescriptor específicos que asignen todos los IDs de credenciales registrados para el usuario que accedió. Si dejas este campo vacío o lo omites, se volverá a las credenciales detectables, lo que infringe la seguridad de la sesión.
// 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 del extremo de verificación
Verifica la aserción que devuelve el cliente:
Verificar la propiedad de la cuenta: El extremo de verificación DEBE verificar de forma explícita que el ID de credencial autenticado resultante que devuelve el cliente se resuelva en un registro de credencial almacenado cuyo ID de usuario asociado coincida estrictamente con el usuario que accedió de forma activa (storedCredential.passkeyUserId === req.user.id). Si se devuelve una llave de acceso válida de un usuario diferente, la autenticación DEBE rechazarse de inmediato.
Deltas de flujo del cliente
Las aplicaciones eligen entre dos interfaces de reautenticación según la IU de la transacción:
A. Flujo de botones (sin campos de entrada)
Activa la reautenticación cuando un usuario presione un botón "Verificar identidad" o "Continuar con la transacción".
<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);
Estrategias de resguardo
Resguardo de la detección de funciones de llaves de acceso
Instala siempre "webauthn-polyfills" y, luego, impórtalo en el contexto.
Ten en cuenta que, siempre que se admita PublicKeyCredential, también se admitirá PublicKeyCredential.getClientCapabilities.
import 'webauthn-polyfills';
Resguardo de serialización de JSON sencillo
Instala siempre "webauthn-polyfills" y, luego, impórtalo en el contexto.
Ten en cuenta que, siempre que se admita PublicKeyCredential, también se admitirán PublicKeyCredential.parseRequestOptionsFromJSON y PublicKeyCredential.prototype.toJSON.
import 'webauthn-polyfills';