Start · Sprachen · JavaScript · Referenz · PublicKeyCredential

PublicKeyCredential

Schnittstelle

Steht für ein Schlüsselpaar aus öffentlichem und privatem Schlüssel, das über WebAuthn eine passwortlose, phishing-resistente Anmeldung ermöglicht (Passkeys, Sicherheitsschlüssel).

seit JavaScript WebAuthn Level 1 – Baseline: alle modernen Browser Kategorie: webapi

Signatur

interface PublicKeyCredential extends Credential

Beschreibung

Die Schnittstelle PublicKeyCredential gehört zur Web Authentication API (WebAuthn) und erweitert Credential aus der Credential Management API. Ein solches Objekt bekommst du, wenn du mit navigator.credentials.create({ publicKey }) ein neues Schlüsselpaar anlegst (Registrierung) oder dich mit navigator.credentials.get({ publicKey }) anmeldest. Der private Schlüssel bleibt immer im Authenticator, also im Gerät (Touch ID, Windows Hello, Android) oder auf einem Hardware-Token wie dem YubiKey. Dein Server bekommt nur den öffentlichen Schlüssel und signierte Daten zu sehen.

Wichtige Instanz-Eigenschaften:

  • id / rawId: die Kennung der Anmeldeinformation, als Base64URL-String bzw. als ArrayBuffer.
  • type: immer "public-key".
  • response: bei der Registrierung ein AuthenticatorAttestationResponse, bei der Anmeldung ein AuthenticatorAssertionResponse.
  • authenticatorAttachment: "platform" (im Gerät eingebaut) oder "cross-platform" (z. B. USB-Token, Smartphone).

Instanz-Methoden sind getClientExtensionResults() und toJSON() (liefert ein serialisierbares Objekt mit Base64URL-kodierten Feldern). Dazu kommen statische Methoden: isUserVerifyingPlatformAuthenticatorAvailable(), isConditionalMediationAvailable() (Passkey-Autofill), getClientCapabilities() sowie parseCreationOptionsFromJSON() und parseRequestOptionsFromJSON().

Einen Konstruktor gibt es nicht, new PublicKeyCredential() wirft einen TypeError. Die Objekte entstehen nur über navigator.credentials. Die Challenge muss dein Server erzeugen, und er muss auch die Antwort prüfen: Signatur, Origin, RP-ID und Signaturzähler.

Parameter

Name Typ Default Beschreibung
$options.publicKey (create) Pflicht PublicKeyCredentialCreationOptions Wird an navigator.credentials.create() übergeben. Enthält challenge, rp (Relying Party), user, pubKeyCredParams (Algorithmen, z. B. -7 für ES256 und -257 für RS256), optional authenticatorSelection, timeout, attestation und excludeCredentials.
$options.publicKey (get) Pflicht PublicKeyCredentialRequestOptions Wird an navigator.credentials.get() übergeben. Enthält challenge, optional rpId, allowCredentials, userVerification und timeout.
$options.mediation "silent" | "optional" | "conditional" | "required" "optional" Mit "conditional" erscheinen Passkeys im Autofill eines Eingabefelds mit autocomplete="username webauthn".
$options.signal AbortSignal Damit lässt sich eine laufende Zeremonie abbrechen, z. B. wenn der Autofill-Vorgang durch einen expliziten Login ersetzt werden soll.

Beispiele

Unterstützung prüfen

const checkPasskeySupport = async () => {
  if (!window.PublicKeyCredential) {
    return { webauthn: false };
  }
  const [platform, autofill] = await Promise.all([
    PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable(),
    PublicKeyCredential.isConditionalMediationAvailable?.() ?? false
  ]);
  return { webauthn: true, platform, autofill };
};

checkPasskeySupport().then(console.log);
{ webauthn: true, platform: true, autofill: true }

Registrierung: Passkey anlegen und an den Server senden

const registerPasskey = async () => {
  // Optionen (inkl. Challenge) liefert der Server als JSON
  const optionsJSON = await fetch('/webauthn/register/options').then(r => r.json());
  const publicKey = PublicKeyCredential.parseCreationOptionsFromJSON(optionsJSON);

  const credential = await navigator.credentials.create({ publicKey });
  console.log(credential.type, credential.authenticatorAttachment);

  // toJSON() kodiert alle ArrayBuffer als Base64URL
  await fetch('/webauthn/register/verify', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(credential.toJSON())
  });
};

registerPasskey().catch(err => {
  if (err.name === 'NotAllowedError') console.warn('Vom Nutzer abgebrochen');
  else if (err.name === 'InvalidStateError') console.warn('Passkey existiert bereits');
  else console.error(err);
});
public-key platform

Anmeldung ohne Hilfsmethoden (manuelle Kodierung)

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

const login = async (challenge /* Uint8Array vom Server */) => {
  const cred = await navigator.credentials.get({
    publicKey: {
      challenge,
      rpId: location.hostname,
      userVerification: 'preferred',
      timeout: 60000
    }
  });

  const payload = {
    id: cred.id,
    rawId: b64url(cred.rawId),
    type: cred.type,
    response: {
      clientDataJSON: b64url(cred.response.clientDataJSON),
      authenticatorData: b64url(cred.response.authenticatorData),
      signature: b64url(cred.response.signature),
      userHandle: cred.response.userHandle ? b64url(cred.response.userHandle) : null
    }
  };
  return fetch('/webauthn/login/verify', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(payload)
  });
};

Passkey-Autofill (Conditional UI)

// HTML: <input name="username" autocomplete="username webauthn">
const controller = new AbortController();

const startAutofill = async () => {
  if (!(await PublicKeyCredential.isConditionalMediationAvailable?.())) return;
  const optionsJSON = await fetch('/webauthn/login/options').then(r => r.json());
  const cred = await navigator.credentials.get({
    mediation: 'conditional',
    signal: controller.signal,
    publicKey: PublicKeyCredential.parseRequestOptionsFromJSON(optionsJSON)
  });
  console.log('Angemeldet mit', cred.id);
};

startAutofill().catch(e => e.name !== 'AbortError' && console.error(e));
// Vor einem anderen Login-Weg: controller.abort();
Angemeldet mit AbCdEf…