Bedingte Passkey-Erstellung (Werbung nach der Anmeldung)

In diesem Leitfaden wird beschrieben, wie Sie einen Passkey für einen Nutzer sofort nach einer erfolgreichen passwortbasierten Anmeldung automatisch und im Hintergrund registrieren. So können Sie die Reibung minimieren und die Akzeptanz von Passkeys erhöhen.

Der richtige Trigger-Moment

Die automatische Passkey-Erstellung (auch als bedingte Erstellung oder unauffällige Aufforderung nach der Anmeldung bezeichnet) DARF nur unmittelbar nach einer erfolgreichen, vollständigen Anmeldung mit einem Passwort ausgelöst werden.

  • Versuchen Sie nicht, eine bedingte Erstellung für passwortlose Abläufe (z.B. Magic Links, SMS-OTP oder Identitätsföderation) durchzuführen.
  • Wenn eine Multi-Faktor-Authentifizierung erforderlich ist, MÜSSEN Sie warten, bis alle Faktoren erfolgreich abgeschlossen wurden, bevor Sie die bedingte Erstellung initiieren.
  • Achten Sie darauf, dass eine gültige, authentifizierte Nutzersitzung aktiv ist, bevor Sie Anfragen an Erstellungsendpunkte senden.

Implementierungsschritte

1. Vorherige Autofill-Aktionen abbrechen

Wenn auf der Anmeldeseite das automatische Ausfüllen von Formularen (Conditional UI/Get) verwendet wird, muss der aktive Abruf der Anmeldedaten abgebrochen werden, um Browserkonflikte zu vermeiden.

  • Rufe abortController.abort() für die AbortController auf, die an die ausstehende navigator.credentials.get()-Anfrage zum automatischen Ausfüllen angehängt ist, bevor du navigator.credentials.create() aufrufst.

2. Feature-Erkennung

Prüfen Sie mit conditionalCreate und PublicKeyCredential.getClientCapabilities(), ob die bedingte Erstellung verfügbar ist.

const capabilities = await PublicKeyCredential.getClientCapabilities();
if (capabilities.conditionalCreate) {
  // Conditional create is available
}

3. Passkey mit bedingter Erstellung erstellen

  • Übergeben Sie mediation: 'conditional' in den navigator.credentials.create()-Optionen. Dadurch wird der Browser angewiesen, den Passkey-Erstellungsprozess im Hintergrund oder kontextbezogen zu verarbeiten, ohne aufdringliche modale Dialogfelder anzuzeigen.
  • Füllen Sie excludeCredentials mit den vorhandenen Passkey-Anmeldedaten-IDs des Nutzers, um die Registrierung doppelter Schlüssel zu vermeiden.

4. Stille Fehlerbehandlung

  • Fügen Sie die Aufforderung zum Erstellen eines Passkeys (navigator.credentials.create) in einen try/catch-Block ein. Sie MÜSSEN typische für Nutzer sichtbare Ausnahmen (InvalidStateError, NotAllowedError, AbortError) abfangen und ignorieren, ohne dem Nutzer eine Fehler-UI anzuzeigen.

5. Serverseitige Überprüfung der Nutzeranwesenheit

  • Der Endpunkt für die serverseitige Überprüfung MUSS die Anforderung zur Nutzerpräsenz (requireUserPresence: false) NUR dann lockern, wenn Anmeldedaten überprüft werden, die durch einen Trigger für die bedingte Erstellung erzeugt wurden. Die strenge Anwesenheitsbestätigung muss für standardmäßige explizite Erstellungen aktiv bleiben.

6. Fehlgeschlagene Serverüberprüfung ordnungsgemäß verarbeiten

  • Wenn navigator.credentials.create() erfolgreich ist, aber der Abruf der Serverbestätigung eine fehlerhafte Antwort zurückgibt (z.B. wenn die Signaturprüfung fehlschlägt), rufen Sie PublicKeyCredential.signalUnknownCredential() auf, um zu verhindern, dass verwaiste Anmeldedaten im Passkey-Anbieter verbleiben.

Codebeispiel

// optionsFetch and registerVerifyFetch are app-defined server endpoint requests
import { optionsFetch, registerVerifyFetch } from './api.js';

async function triggerConditionalCreate(loginAbortController) {
  const capabilities = await PublicKeyCredential.getClientCapabilities();
  if (capabilities.conditionalCreate !== true) {
    return; // Platform does not support conditional creation
  }

  // 1. Abort any active autofill conditional-get controllers to clear the WebAuthn pipeline
  loginAbortController.abort();

  // 2. Fetch creation options signaling the backend that this is a conditional request
  const creationOptionsJSON = await optionsFetch({ conditional: true });
  const publicKey = PublicKeyCredential.parseCreationOptionsFromJSON(creationOptionsJSON);

  let credential;
  try {
    // 3. Invoke silent credentials creation prompt
    credential = await navigator.credentials.create({ 
      publicKey,
      mediation: 'conditional' // Silent background creation mediation
    });
  } catch (e) {
    // 4. Silently swallow common WebAuthn browser exceptions
    if (['InvalidStateError', 'NotAllowedError', 'AbortError'].includes(e.name)) {
      return; 
    }
    console.error('Unexpected conditional create error:', e);
    return;
  }

  // 5. Server verification step using dedicated Try/Catch block
  let encodedResponse = credential.toJSON();
  try {
    const response = await registerVerifyFetch(encodedResponse);
    if (!response.ok) {
      // If the server verification fails, clean up using Signal API
      if (PublicKeyCredential.signalUnknownCredential) {
        await PublicKeyCredential.signalUnknownCredential({
          rpId, // RP ID must match the one defined on the server
          credentialId: encodedResponse.id
        });
      }
    }
  } catch (serverErr) {
    console.error('Verification network failure:', serverErr);
    if (PublicKeyCredential.signalUnknownCredential) {
      await PublicKeyCredential.signalUnknownCredential({
        rpId, // RP ID must match the one defined on the server
        credentialId: encodedResponse.id
      });
    }
  }
}

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.parseCreationOptionsFromJSON und PublicKeyCredential.prototype.toJSON unterstützt.

import 'webauthn-polyfills';