Signatur
Beschreibung
Die schreibgeschützte Eigenschaft response eines PublicKeyCredential enthält die Antwort des Authenticators (z. B. Sicherheitsschlüssel, Touch ID, Windows Hello oder ein Passkey auf dem Smartphone). Diese Daten schickt die Webanwendung an den Server. Dort werden sie geprüft, um einen neuen Schlüssel zu registrieren oder eine Anmeldung zu bestätigen.
Welcher Typ zurückkommt, hängt von der aufgerufenen Methode ab:
navigator.credentials.create()(Registrierung) liefert eineAuthenticatorAttestationResponsemitclientDataJSON,attestationObjectsowie den MethodengetPublicKey(),getAuthenticatorData(),getPublicKeyAlgorithm()undgetTransports().navigator.credentials.get()(Anmeldung) liefert eineAuthenticatorAssertionResponsemitclientDataJSON,authenticatorData,signatureunduserHandle.
Fast alle Felder sind ArrayBuffer-Objekte. Für die Übertragung per JSON müssen sie deshalb erst kodiert werden, üblicherweise als Base64URL. Die eigentliche Prüfung (Challenge, Origin, Signatur, Zähler) muss immer auf dem Server passieren. Dem Client allein darf man hier nicht vertrauen.
Rückgabewert
AuthenticatorResponse-Objekt: bei create() eine AuthenticatorAttestationResponse, bei get() eine AuthenticatorAssertionResponse.Beispiele
Registrierung: Attestation-Daten auslesen und an den Server senden
const toB64Url = (buf) => btoa(String.fromCharCode(...new Uint8Array(buf)))
.replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
const registerPasskey = async (options) => {
// options kommen vom Server (challenge, user.id als ArrayBuffer usw.)
const credential = await navigator.credentials.create({ publicKey: options });
const { response } = credential;
console.log(response instanceof AuthenticatorAttestationResponse);
await fetch('/webauthn/register', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
id: credential.id,
rawId: toB64Url(credential.rawId),
type: credential.type,
response: {
clientDataJSON: toB64Url(response.clientDataJSON),
attestationObject: toB64Url(response.attestationObject),
transports: response.getTransports?.() ?? []
}
})
});
};
Anmeldung: Assertion-Daten übertragen
const toB64Url = (buf) => btoa(String.fromCharCode(...new Uint8Array(buf)))
.replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
const login = async (challenge) => {
const credential = await navigator.credentials.get({
publicKey: { challenge, userVerification: 'preferred' }
});
const { response } = credential; // AuthenticatorAssertionResponse
const payload = {
id: credential.id,
response: {
clientDataJSON: toB64Url(response.clientDataJSON),
authenticatorData: toB64Url(response.authenticatorData),
signature: toB64Url(response.signature),
userHandle: response.userHandle ? toB64Url(response.userHandle) : null
}
};
const res = await fetch('/webauthn/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload)
});
return res.ok;
};
clientDataJSON zu Debugzwecken dekodieren
const inspectClientData = (credential) => {
const json = new TextDecoder().decode(credential.response.clientDataJSON);
const clientData = JSON.parse(json);
console.log(clientData.type, clientData.origin);
return clientData;
};
Moderne Abkürzung mit toJSON()
const credential = await navigator.credentials.get({ publicKey: options });
// Neuere Browser serialisieren response automatisch als Base64URL
const body = typeof credential.toJSON === 'function'
? JSON.stringify(credential)
: null; // sonst manuell kodieren (siehe oben)
console.log(body !== null);
// Wichtig · Fallstricke
- Nur Rohdaten: Die Werte in
responsesindArrayBuffer. Ein direktesJSON.stringify(response)ergibt leere Objekte ({}). Man muss manuell kodieren oderPublicKeyCredential.prototype.toJSON()verwenden, das es erst in neueren Browsern gibt. - Serverseitige Prüfung ist Pflicht: Challenge,
origin, RP-ID-Hash, Flags (User Present/Verified), Signatur und Signaturzähler müssen auf dem Server geprüft werden. Sonst lässt sich die Authentifizierung umgehen. - Typ prüfen: Welche Felder vorhanden sind, hängt vom Aufruf ab.
signaturegibt es nur bei der Assertion,attestationObjectnur bei der Attestation. Im Zweifel mitinstanceofunterscheiden. userHandlekannnullsein, wenn es kein auffindbares Credential (Passkey) ist.- Die Methoden
getPublicKey(),getTransports()undgetAuthenticatorData()kamen erst mit WebAuthn Level 2. In älteren Browsern vorher auf ihr Vorhandensein prüfen. - WebAuthn läuft nur in einem sicheren Kontext (HTTPS oder
localhost). In iframes funktioniert es nur mit passender Permissions-Policy (publickey-credentials-get/-create).