Start · Sprachen · JavaScript · Referenz · AuthenticatorAssertionResponse

AuthenticatorAssertionResponse

Schnittstelle

Enthält die vom Authenticator erzeugte digitale Signatur samt Metadaten, mit der ein Server eine WebAuthn-Anmeldung (Assertion) prüft.

seit JavaScript Baseline: alle modernen Browser (Chrome 67+, Firef Kategorie: webapi

Signatur

interface AuthenticatorAssertionResponse extends AuthenticatorResponse

Beschreibung

AuthenticatorAssertionResponse ist Teil der Web Authentication API (WebAuthn). Bei einer Anmeldung mit Passkey oder Sicherheitsschlüssel ruft man navigator.credentials.get({ publicKey }) auf. Das Promise wird mit einem PublicKeyCredential erfüllt. Dessen Eigenschaft response ist eine Instanz dieser Schnittstelle. Sie enthält den Beweis, dass der Nutzer den privaten Schlüssel besitzt: Der Authenticator hat die Challenge des Servers zusammen mit weiteren Daten signiert.

Die Schnittstelle erbt von AuthenticatorResponse und stellt diese Eigenschaften bereit (alle schreibgeschützt):

  • clientDataJSON (ArrayBuffer, geerbt): UTF-8-kodiertes JSON mit type ("webauthn.get"), challenge und origin.
  • authenticatorData (ArrayBuffer): Hash der RP-ID, Flags (z. B. User Present und User Verified) sowie der Signaturzähler.
  • signature (ArrayBuffer): Signatur über authenticatorData || SHA-256(clientDataJSON), erstellt mit dem privaten Schlüssel des Credentials.
  • userHandle (ArrayBuffer | null): Die bei der Registrierung vergebene Benutzer-ID (user.id). Bei auffindbaren Credentials (Passkeys) ist sie in der Regel gesetzt.
  • attestationObject (ArrayBuffer | null): Nur vorhanden, wenn bei der Assertion eine Attestierung angefordert wurde (neuere Spezifikation, selten unterstützt).

Instanzen erzeugt ausschließlich der Browser. Es gibt keinen öffentlichen Konstruktor. Im Client werden die Binärdaten nur kodiert (meist Base64URL) und an den Server geschickt. Die eigentliche Prüfung der Signatur mit dem gespeicherten öffentlichen Schlüssel, der Challenge, des Origins, der RP-ID und des Zählers muss immer serverseitig stattfinden.

Rückgabewert

Typ
AuthenticatorAssertionResponse
Beschreibung
Wird nicht direkt erzeugt, sondern ist über PublicKeyCredential.response erreichbar, nachdem navigator.credentials.get() erfolgreich war.

Beispiele

Anmeldung mit Passkey und Zugriff auf die Assertion

// Challenge kommt vom Server (hier nur zur Demonstration zufällig erzeugt)
const challenge = crypto.getRandomValues(new Uint8Array(32));

const credential = await navigator.credentials.get({
  publicKey: {
    challenge,
    rpId: location.hostname,
    userVerification: 'preferred',
    timeout: 60000
  }
});

const response = credential.response; // AuthenticatorAssertionResponse
console.log(response instanceof AuthenticatorAssertionResponse);
console.log(response.signature.byteLength > 0);
console.log(response.userHandle !== null);
true true true

clientDataJSON und authenticatorData clientseitig lesen (Debugging)

const inspectAssertion = (response) => {
  const clientData = JSON.parse(new TextDecoder().decode(response.clientDataJSON));
  const authData = new Uint8Array(response.authenticatorData);

  const flags = authData[32];
  const userPresent = Boolean(flags & 0x01);
  const userVerified = Boolean(flags & 0x04);
  const signCount = new DataView(response.authenticatorData).getUint32(33, false);

  return { type: clientData.type, origin: clientData.origin, userPresent, userVerified, signCount };
};

const cred = await navigator.credentials.get({
  publicKey: { challenge: crypto.getRandomValues(new Uint8Array(32)) }
});
console.log(inspectAssertion(cred.response));
{ type: 'webauthn.get', origin: 'https://example.com', userPresent: true, userVerified: true, signCount: 0 }

Assertion Base64URL-kodiert an den Server senden

const toBase64Url = (buffer) =>
  btoa(String.fromCharCode(...new Uint8Array(buffer)))
    .replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');

const login = async () => {
  const { challenge } = await (await fetch('/webauthn/login/options')).json();
  const cred = await navigator.credentials.get({
    publicKey: {
      challenge: Uint8Array.from(atob(challenge.replace(/-/g, '+').replace(/_/g, '/')), c => c.charCodeAt(0))
    }
  });

  const { response } = cred;
  const payload = {
    id: cred.id,
    rawId: toBase64Url(cred.rawId),
    type: cred.type,
    response: {
      clientDataJSON: toBase64Url(response.clientDataJSON),
      authenticatorData: toBase64Url(response.authenticatorData),
      signature: toBase64Url(response.signature),
      userHandle: response.userHandle ? toBase64Url(response.userHandle) : null
    }
  };

  const res = await fetch('/webauthn/login/verify', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(payload)
  });
  console.log(res.ok ? 'Angemeldet' : 'Prüfung fehlgeschlagen');
};

login();
Angemeldet

Kurzform mit toJSON()

// Neuere Browser serialisieren das Credential direkt (Base64URL)
const cred = await navigator.credentials.get({
  publicKey: { challenge: crypto.getRandomValues(new Uint8Array(32)) }
});
const json = cred.toJSON();
console.log(Object.keys(json.response));
['clientDataJSON', 'authenticatorData', 'signature', 'userHandle']

// Wichtig · Fallstricke

  • Sicherheit: Verlass dich nie auf eine Prüfung im Client. Der Server muss prüfen, dass die Challenge stimmt und nur einmal verwendet wird, dass der origin und der RP-ID-Hash passen und dass die Flags (UP/UV) gesetzt sind. Außerdem muss er die Signatur mit dem gespeicherten öffentlichen Schlüssel prüfen. Die Challenge sollte vom Server kommen, kryptografisch zufällig sein und nur kurz gelten.
  • Signaturformat: Bei ES256 ist die Signatur ASN.1/DER-kodiert und nicht roh (r||s). Wer mit crypto.subtle.verify() prüft, muss sie vorher umwandeln.
  • Signaturzähler: Viele Passkey-Anbieter (z. B. synchronisierte Passkeys von Apple oder Google) liefern immer 0. Ein Zähler von 0 ist also kein Hinweis auf ein geklontes Credential.
  • userHandle kann bei nicht auffindbaren Credentials (mit allowCredentials) null sein.
  • Sicherer Kontext: WebAuthn funktioniert nur über HTTPS (oder localhost). In Cross-Origin-iframes ist es nur mit der Permissions-Policy publickey-credentials-get verfügbar.
  • Kompatibilität: Wird in allen modernen Browsern unterstützt. attestationObject und PublicKeyCredential.prototype.toJSON() sind neuer und nicht überall vorhanden. Prüfe vorher auf ihre Verfügbarkeit.