Erneute Authentifizierung mit Passkey

Webfunktionen: Webauthentifizierung

In diesem Leitfaden wird beschrieben, wie Sie eine zusätzliche Authentifizierung oder erneute Bestätigung für einen angemeldeten Nutzer implementieren, bevor er sensible Kontoänderungen vornimmt (z.B. Passwortaktualisierungen, Finanztransaktionen).

Delta-Flussarchitektur

Im Gegensatz zur regulären Authentifizierung werden bei der erneuten Authentifizierung mit Passkeys die Passkey-Dialogaufforderungen strikt auf die vorab registrierten Anmeldedaten des angemeldeten Nutzers beschränkt, um eine Kontovermischung oder Passkey-Spoofing während aktiver Sitzungen zu verhindern.

Serverseitig

Delta für die Generierung von Optionen

Erstellen Sie einen Endpunkt, der die Parameter für zulässige Anmeldedaten speziell für den aktiven, bekannten Nutzer füllt:

Anmeldedaten einschränken: Füllen Sie das Array mit den allowCredentials-Optionen mit bestimmten PublicKeyCredentialDescriptor-Einträgen, die alle registrierten Anmeldedaten-IDs für den angemeldeten Nutzer zuordnen. Wenn Sie dieses Feld leer lassen oder weglassen, wird auf auffindbare Anmeldedaten zurückgegriffen, was die Sitzungssicherheit beeinträchtigt.

// 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 des Bestätigungsendpunkts

Prüfen Sie die vom Client zurückgegebene Assertion:

Kontoinhaberschaft bestätigen: Der Bestätigungsendpunkt MUSS explizit prüfen, ob die vom Client zurückgegebene authentifizierte Anmeldedaten-ID einem gespeicherten Anmeldedatensatz zugeordnet ist, dessen zugehörige Nutzer-ID genau mit dem aktiven angemeldeten Nutzer (storedCredential.passkeyUserId === req.user.id) übereinstimmt. Wenn ein gültiger Passkey eines anderen Nutzers zurückgegeben wird, MUSS die Authentifizierung sofort abgelehnt werden.

Clientseitige Flussdeltas

Anwendungen wählen je nach Transaktions-UI zwischen zwei Schnittstellen für die erneute Authentifizierung:

A. Button Flow (No Input Fields)

Lösen Sie die erneute Authentifizierung aus, wenn ein Nutzer auf die Schaltfläche „Identität bestätigen“ oder „Transaktion fortsetzen“ klickt.

<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);

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';

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';