Signatur
Beschreibung
Die Methode getClientExtensionResults() eines PublicKeyCredential-Objekts liefert eine Zuordnung (Map-artiges Objekt) von Erweiterungskennungen zu ihren Ergebnissen. Welche Erweiterungen aktiv sind, legt die Anwendung über die Option extensions in navigator.credentials.create({ publicKey }) bzw. navigator.credentials.get({ publicKey }) fest.
Häufig verwendete Erweiterungen sind:
credProps: zeigt überrkan, ob ein auffindbarer Schlüssel (Passkey/Resident Key) angelegt wurde.prf: leitet aus dem Authenticator geheime Werte ab, z. B. für Ende-zu-Ende-Verschlüsselung.largeBlob: liest oder schreibt kleine Datenmengen, die zusammen mit dem Schlüssel gespeichert werden.appid: Rückwärtskompatibilität mit alten FIDO-U2F-Registrierungen.minPinLength,credentialProtectionPolicyu. a.
Wichtig: Diese Methode gibt nur die Client-Erweiterungsergebnisse zurück, also die Werte, die der Browser selbst erzeugt oder weiterreicht. Die Authenticator-Erweiterungsergebnisse stecken dagegen signiert in authenticatorData und müssen auf dem Server ausgewertet werden. Da die Client-Ergebnisse nicht signiert sind, dürfen sie auf dem Server nicht als vertrauenswürdig angesehen werden.
Rückgabewert
credProps, prf) und dessen Werte die jeweiligen Ergebnisse enthalten. Nicht unterstützte oder nicht angeforderte Erweiterungen fehlen; ohne Erweiterungen ist es ein leeres Objekt {}.Beispiele
Prüfen, ob ein Passkey (Resident Key) erstellt wurde
const credential = await navigator.credentials.create({
publicKey: {
challenge: crypto.getRandomValues(new Uint8Array(32)),
rp: { name: 'Beispiel GmbH', id: location.hostname },
user: {
id: new TextEncoder().encode('user-42'),
name: 'anna@example.com',
displayName: 'Anna'
},
pubKeyCredParams: [{ type: 'public-key', alg: -7 }, { type: 'public-key', alg: -257 }],
authenticatorSelection: { residentKey: 'preferred' },
extensions: { credProps: true }
}
});
const results = credential.getClientExtensionResults();
console.log(results);
if (results.credProps?.rk) {
console.log('Passkey gespeichert – Login ohne Benutzername möglich.');
} else {
console.log('Kein auffindbarer Schlüssel – Benutzername weiterhin nötig.');
}
PRF-Erweiterung bei der Anmeldung auswerten
const salt = new TextEncoder().encode('verschluesselung-v1');
const assertion = await navigator.credentials.get({
publicKey: {
challenge: crypto.getRandomValues(new Uint8Array(32)),
rpId: location.hostname,
userVerification: 'required',
extensions: { prf: { eval: { first: salt } } }
}
});
const { prf } = assertion.getClientExtensionResults();
if (prf?.results?.first) {
// ArrayBuffer mit 32 Byte – als Schlüsselmaterial nutzbar
const key = await crypto.subtle.importKey(
'raw', prf.results.first, 'HKDF', false, ['deriveKey']
);
console.log('PRF-Schlüssel abgeleitet:', key.type);
} else {
console.log('Authenticator unterstützt PRF nicht.');
}
Ergebnisse zum Server senden
const sendToServer = async (credential) => {
const toB64 = (buf) => btoa(String.fromCharCode(...new Uint8Array(buf)));
const payload = {
id: credential.id,
type: credential.type,
rawId: toB64(credential.rawId),
response: {
clientDataJSON: toB64(credential.response.clientDataJSON),
authenticatorData: toB64(credential.response.authenticatorData),
signature: toB64(credential.response.signature)
},
// Nur informativ – nicht signiert!
clientExtensionResults: credential.getClientExtensionResults()
};
await fetch('/webauthn/verify', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload)
});
};
// Wichtig · Fallstricke
- Nicht vertrauenswürdig: Client-Erweiterungsergebnisse sind nicht vom Authenticator signiert. Sicherheitsentscheidungen auf dem Server sollten sich auf die signierten Daten in
authenticatorDatastützen. - ArrayBuffer in JSON: Ergebnisse wie
prf.results.firstoderlargeBlob.blobsindArrayBufferund werden vonJSON.stringify()zu{}. Vorher nach Base64URL umwandeln oderPublicKeyCredential.prototype.toJSON()verwenden (sofern verfügbar). - Unterstützung variiert: Die Methode selbst ist in allen modernen Browsern vorhanden, die einzelnen Erweiterungen (z. B.
prf,largeBlob) aber nicht überall. Fehlende Unterstützung äußert sich still durch fehlende Schlüssel – immer mit optionaler Verkettung (?.) prüfen. - Die Methode steht nur in einem sicheren Kontext (HTTPS oder
localhost) zur Verfügung. - Jeder Aufruf liefert ein neues Objekt; Änderungen daran wirken sich nicht auf das Credential aus.