Signatur
Beschreibung
PublicKeyCredentialCreationOptions ist kein Konstruktor, sondern ein einfaches Objekt (WebIDL-Dictionary). Du übergibst es als Wert der Eigenschaft publicKey an navigator.credentials.create(). Damit registrierst du über die Web Authentication API (WebAuthn) ein neues asymmetrisches Schlüsselpaar bei einem Authenticator, zum Beispiel einem Passkey im Betriebssystem, einem Sicherheitsschlüssel wie dem YubiKey oder Touch ID/Windows Hello.
Das Dictionary beschreibt die Relying Party (rp, also deine Website), den Benutzer (user), eine vom Server erzeugte Einmal-Challenge, die erlaubten Signaturalgorithmen (pubKeyCredParams) und optional Anforderungen an den Authenticator. Dazu gehören z. B. Plattform- oder externer Authenticator, auffindbare Credentials (Passkeys) und Benutzerverifikation per PIN oder Biometrie.
Normalerweise erzeugt der Server die Optionen, vor allem challenge und user.id. Der Client wandelt sie in ArrayBuffers um und reicht sie weiter. Die zurückgegebene PublicKeyCredential geht zur Prüfung an den Server zurück. Neuere Browser haben dafür PublicKeyCredential.parseCreationOptionsFromJSON(), das die Base64URL-Umwandlung übernimmt.
Parameter
| Name | Typ | Default | Beschreibung |
|---|---|---|---|
| $rp Pflicht | { id?: string, name: string } | Relying Party. name ist ein lesbarer Name. id ist die Domain, für die das Credential gilt (Standard: aktuelle Origin-Domain). Sie muss mit der aktuellen Domain übereinstimmen oder ein registrierbares Suffix davon sein. |
|
| $user Pflicht | { id: BufferSource, name: string, displayName: string } | Benutzerkonto. id ist ein undurchsichtiges Handle mit höchstens 64 Byte und ohne personenbezogene Daten. name ist z. B. eine E-Mail-Adresse, displayName der Anzeigename. |
|
| $challenge Pflicht | BufferSource | Kryptografisch zufälliger Wert vom Server (mind. 16 Byte). Er schützt vor Replay-Angriffen. | |
| $pubKeyCredParams Pflicht | Array<{ type: "public-key", alg: number }> | Erlaubte Algorithmen als COSE-IDs, nach Vorliebe sortiert. Beispiele: -7 (ES256), -257 (RS256), -8 (EdDSA). |
|
| $timeout | number | browserabhängig | Empfohlene maximale Wartezeit in Millisekunden. Der Browser behandelt den Wert nur als Hinweis. |
| $excludeCredentials | Array<{ type: "public-key", id: BufferSource, transports?: string[] }> | [] | Bereits registrierte Credentials des Benutzers. Damit verhinderst du, dass derselbe Authenticator doppelt registriert wird. |
| $authenticatorSelection | { authenticatorAttachment?: "platform"|"cross-platform", residentKey?: "discouraged"|"preferred"|"required", requireResidentKey?: boolean, userVerification?: "required"|"preferred"|"discouraged" } | {} | Anforderungen an den Authenticator. Für echte Passkeys setzt du residentKey: "required" und userVerification: "required" oder "preferred". |
| $attestation | "none"|"indirect"|"direct"|"enterprise" | "none" | Legt fest, ob und wie der Authenticator ein Attestation Statement über seine Herkunft liefern soll. |
| $attestationFormats | string[] | [] | Bevorzugte Attestation-Formate, z. B. "packed" oder "tpm" (WebAuthn Level 3). |
| $hints | Array<"security-key"|"client-device"|"hybrid"> | [] | UI-Hinweise an den Browser, welche Art von Authenticator bevorzugt wird (WebAuthn Level 3, noch nicht überall unterstützt). |
| $extensions | object | Client-Erweiterungen, z. B. { credProps: true }, { prf: {} } oder { largeBlob: { support: "preferred" } }. |
Rückgabewert
navigator.credentials.create({ publicKey }), bekommst du ein Promise<PublicKeyCredential | null> mit einer AuthenticatorAttestationResponse.Beispiele
Minimale Passkey-Registrierung
const registerPasskey = async () => {
const publicKey = {
challenge: crypto.getRandomValues(new Uint8Array(32)), // in der Praxis vom Server!
rp: { name: 'Beispiel GmbH', id: location.hostname },
user: {
id: crypto.getRandomValues(new Uint8Array(16)),
name: 'max@example.com',
displayName: 'Max Mustermann'
},
pubKeyCredParams: [
{ type: 'public-key', alg: -7 }, // ES256
{ type: 'public-key', alg: -257 } // RS256
],
authenticatorSelection: {
residentKey: 'required',
userVerification: 'preferred'
},
timeout: 60000,
attestation: 'none'
};
const credential = await navigator.credentials.create({ publicKey });
console.log(credential.type, credential.id);
};
document.querySelector('#register').addEventListener('click', registerPasskey);
Optionen vom Server laden und Base64URL umwandeln
const b64urlToBuffer = (s) => {
const b64 = s.replace(/-/g, '+').replace(/_/g, '/').padEnd(Math.ceil(s.length / 4) * 4, '=');
return Uint8Array.from(atob(b64), (c) => c.charCodeAt(0)).buffer;
};
const register = async () => {
const res = await fetch('/webauthn/register/options', { method: 'POST' });
const json = await res.json();
// Moderner Weg, falls verfügbar
const publicKey = PublicKeyCredential.parseCreationOptionsFromJSON
? PublicKeyCredential.parseCreationOptionsFromJSON(json)
: {
...json,
challenge: b64urlToBuffer(json.challenge),
user: { ...json.user, id: b64urlToBuffer(json.user.id) },
excludeCredentials: (json.excludeCredentials ?? []).map((c) => ({
...c,
id: b64urlToBuffer(c.id)
}))
};
const credential = await navigator.credentials.create({ publicKey });
await fetch('/webauthn/register/verify', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(credential.toJSON()) // toJSON(): neuere Browser
});
};
Doppelte Registrierung verhindern und Fehler behandeln
const createWithExclusion = async (options, existingIds) => {
const publicKey = {
...options,
excludeCredentials: existingIds.map((id) => ({ type: 'public-key', id })),
extensions: { credProps: true }
};
try {
const cred = await navigator.credentials.create({ publicKey });
const { credProps } = cred.getClientExtensionResults();
console.log('Auffindbar (Passkey):', credProps?.rk);
return cred;
} catch (err) {
if (err.name === 'InvalidStateError') {
console.warn('Dieser Authenticator ist bereits registriert.');
} else if (err.name === 'NotAllowedError') {
console.warn('Abgebrochen oder Zeitüberschreitung.');
} else {
throw err;
}
}
};