Start · Sprachen · JavaScript · Referenz · AuthenticatorAssertionResponse.userHandle

AuthenticatorAssertionResponse.userHandle

Eigenschaft

Schreibgeschützter <code>ArrayBuffer</code> (oder <code>null</code>) mit dem opaken Benutzer-Bezeichner, der bei der Registrierung als <code>user.id</code> hinterlegt wurde.

seit JavaScript Baseline: seit 2021 in allen modernen Browsern (Ch Kategorie: webapi

Signatur

get AuthenticatorAssertionResponse.userHandle

Beschreibung

Die Eigenschaft userHandle eines AuthenticatorAssertionResponse-Objekts enthält den Benutzer-Bezeichner (user handle), den die Relying Party bei der Registrierung über navigator.credentials.create() im Feld publicKey.user.id übergeben hat. Der Authenticator speichert diesen Wert zusammen mit dem Credential und gibt ihn bei einer Anmeldung über navigator.credentials.get() wieder zurück.

Besonders wichtig ist userHandle bei Passkeys bzw. auffindbaren Credentials (discoverable credentials, früher „resident keys“): Hier fragt die Webseite ohne Benutzernamen und ohne allowCredentials nach einem Credential. Der Server erfährt erst über userHandle, welches Konto sich gerade anmeldet, und kann dann den passenden öffentlichen Schlüssel für die Signaturprüfung laden.

Der Wert ist ein opaker ArrayBuffer mit höchstens 64 Byte. Er sollte keine personenbezogenen Daten (E-Mail-Adresse, Name) enthalten, sondern z. B. eine zufällig erzeugte ID sein. Wird ein nicht auffindbares Credential verwendet, darf der Authenticator den Wert weglassen – dann ist userHandle null.

Rückgabewert

Typ
ArrayBuffer | null
Beschreibung
Ein ArrayBuffer mit dem bei der Registrierung gesetzten user.id oder null, wenn der Authenticator keinen Benutzer-Bezeichner liefert (typisch bei nicht auffindbaren Credentials).

Beispiele

Passkey-Anmeldung ohne Benutzernamen und userHandle auslesen

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

const loginWithPasskey = async (challenge) => {
  const credential = await navigator.credentials.get({
    publicKey: {
      challenge,               // Uint8Array vom Server
      rpId: 'example.com',
      userVerification: 'preferred'
      // kein allowCredentials => auffindbare Credentials (Passkeys)
    }
  });

  const { userHandle } = credential.response;
  if (userHandle === null) {
    throw new Error('Authenticator hat keinen userHandle geliefert');
  }

  console.log('userHandle (Base64URL):', toBase64Url(userHandle));
  return credential;
};
userHandle (Base64URL): 3q2-7w0Q9kRfH1aZ

Assertion inklusive userHandle an den Server senden

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

const sendAssertion = async (credential) => {
  const res = credential.response;
  const payload = {
    id: credential.id,
    rawId: toBase64Url(credential.rawId),
    type: credential.type,
    response: {
      clientDataJSON: toBase64Url(res.clientDataJSON),
      authenticatorData: toBase64Url(res.authenticatorData),
      signature: toBase64Url(res.signature),
      // userHandle kann null sein
      userHandle: res.userHandle ? toBase64Url(res.userHandle) : null
    }
  };

  const response = await fetch('/webauthn/login/finish', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(payload)
  });
  return response.json();
};

Zusammenhang mit der Registrierung: user.id setzen

// Bei der Registrierung: zufällige, nicht personenbezogene ID erzeugen
const userId = crypto.getRandomValues(new Uint8Array(16));

const credential = await navigator.credentials.create({
  publicKey: {
    challenge: crypto.getRandomValues(new Uint8Array(32)),
    rp: { name: 'Beispiel GmbH', id: 'example.com' },
    user: { id: userId, name: 'anna@example.com', displayName: 'Anna' },
    pubKeyCredParams: [{ type: 'public-key', alg: -7 }],
    authenticatorSelection: { residentKey: 'required' }
  }
});

// Später bei navigator.credentials.get() liefert
// credential.response.userHandle genau diese 16 Byte zurück.
console.log(userId.byteLength);
16

// Wichtig · Fallstricke

  • Kann null sein: Bei Credentials, die nicht auffindbar sind (Anmeldung mit allowCredentials), darf der Authenticator den Wert weglassen. Code muss diesen Fall immer abfangen.
  • Keine personenbezogenen Daten: Die Spezifikation verlangt, dass user.id bzw. userHandle keine identifizierenden Informationen wie E-Mail oder Benutzername enthält – nutze zufällige Bytes (max. 64 Byte).
  • Nicht vertrauen, sondern prüfen: Der Server muss sicherstellen, dass der gelieferte userHandle zum Credential (rawId) passt und die Signatur mit dem zugehörigen öffentlichen Schlüssel gültig ist. Der Wert allein ist kein Authentifizierungsnachweis.
  • Binärdaten: ArrayBuffer lässt sich nicht direkt per JSON.stringify() übertragen; vorher Base64URL-kodieren. Neuere Browser bieten dafür PublicKeyCredential.prototype.toJSON().
  • Kompatibilität: WebAuthn funktioniert nur in sicheren Kontexten (HTTPS bzw. localhost). In älteren Browsern ohne WebAuthn-Unterstützung existiert das Interface nicht.