Übersicht
Hier finden Sie eine allgemeine Übersicht über die wichtigsten Schritte bei der Registrierung von Passkeys:
- Definieren Sie Optionen zum Erstellen eines Passkeys. Senden Sie sie an den Client, damit Sie sie an Ihren Passkey-Erstellungsaufruf übergeben können: den WebAuthn-API-Aufruf
navigator.credentials.create
im Web undcredentialManager.createCredential
unter Android. Nachdem der Nutzer die Erstellung des Passkeys bestätigt hat, wird der Aufruf zur Passkey-Erstellung aufgelöst und es wird ein AnmeldedatenobjektPublicKeyCredential
zurückgegeben. - Prüfen Sie die Anmeldedaten und speichern Sie sie auf dem Server.
In den folgenden Abschnitten werden die einzelnen Schritte genauer beschrieben.
Optionen zum Erstellen von Anmeldedaten erstellen
Der erste Schritt auf dem Server ist das Erstellen eines PublicKeyCredentialCreationOptions
-Objekts.
Verwenden Sie dazu Ihre serverseitige FIDO-Bibliothek. In der Regel wird eine Hilfsfunktion angeboten, mit der diese Optionen für Sie erstellt werden können. SimpleWebAuthn bietet beispielsweise generateRegistrationOptions
.
PublicKeyCredentialCreationOptions
sollte alles enthalten, was für die Passkey-Erstellung erforderlich ist: Informationen zum Nutzer, zum RP und eine Konfiguration für die Eigenschaften der Anmeldedaten, die Sie erstellen. Wenn Sie alle diese Elemente definiert haben, übergeben Sie sie nach Bedarf an die Funktion in Ihrer FIDO-serverseitigen Bibliothek, die für das Erstellen des PublicKeyCredentialCreationOptions
-Objekts zuständig ist.
Einige Felder von PublicKeyCredentialCreationOptions
können Konstanten sein. Andere sollten dynamisch auf dem Server definiert werden:
rpId
: Um die RP-ID auf dem Server einzufügen, verwenden Sie serverseitige Funktionen oder Variablen, die Ihnen den Hostnamen Ihrer Webanwendung liefern, z. B.example.com
.user.name
unduser.displayName
:Verwenden Sie zum Ausfüllen dieser Felder die Sitzungsinformationen des angemeldeten Nutzers oder die Informationen des neuen Nutzerkontos, wenn der Nutzer bei der Registrierung einen Passkey erstellt.user.name
ist in der Regel eine E‑Mail-Adresse und für die RP eindeutig.user.displayName
ist ein nutzerfreundlicher Name. Hinweis: Nicht alle Plattformen verwendendisplayName
.user.id
: Ein zufälliger, eindeutiger String, der beim Erstellen des Kontos generiert wird. Er sollte dauerhaft sein, im Gegensatz zu einem Nutzernamen, der bearbeitet werden kann. Die Nutzer-ID identifiziert ein Konto, darf aber keine personenidentifizierbaren Informationen enthalten. Sie haben wahrscheinlich bereits eine Nutzer-ID in Ihrem System. Falls erforderlich, erstellen Sie eine speziell für Passkeys, damit sie keine personenbezogenen Daten enthält.excludeCredentials
: Eine Liste der IDs vorhandener Anmeldedaten, um zu verhindern, dass ein Passkey vom Passkey-Anbieter dupliziert wird. Suchen Sie in Ihrer Datenbank nach vorhandenen Anmeldedaten für diesen Nutzer, um dieses Feld auszufüllen. Weitere Informationen finden Sie unter Verhindern, dass ein neuer Passkey erstellt wird, wenn bereits einer vorhanden ist.challenge
: Bei der Registrierung von Anmeldedaten ist die Challenge nicht relevant, es sei denn, Sie verwenden die Attestierung. Das ist eine fortgeschrittene Technik, um die Identität eines Passkey-Anbieters und die von ihm ausgegebenen Daten zu bestätigen. Auch wenn Sie die Attestierung nicht verwenden, ist die Challenge ein Pflichtfeld. Eine Anleitung zum Erstellen einer sicheren Challenge für die Authentifizierung finden Sie unter Serverseitige Passkey-Authentifizierung.
Codierung und Decodierung

PublicKeyCredentialCreationOptions
, die vom Server gesendet wurden. challenge
, user.id
und excludeCredentials.credentials
müssen serverseitig in base64URL
codiert werden, damit PublicKeyCredentialCreationOptions
über HTTPS bereitgestellt werden kann.PublicKeyCredentialCreationOptions
enthält Felder, die ArrayBuffer
sind und daher nicht von JSON.stringify()
unterstützt werden. Das bedeutet, dass derzeit einige Felder manuell auf dem Server mit base64URL
codiert und dann auf dem Client decodiert werden müssen, um PublicKeyCredentialCreationOptions
über HTTPS bereitzustellen.
- Auf dem Server werden Codierung und Decodierung in der Regel von Ihrer serverseitigen FIDO-Bibliothek übernommen.
- Auf dem Client müssen die Codierung und Decodierung derzeit manuell erfolgen. In Zukunft wird es einfacher sein: Es wird eine Methode geben, mit der Optionen als JSON in
PublicKeyCredentialCreationOptions
konvertiert werden können. Hier können Sie den Status der Implementierung in Chrome einsehen.
Beispielcode: Optionen zum Erstellen von Anmeldedaten erstellen
In unseren Beispielen verwenden wir die SimpleWebAuthn-Bibliothek. Hier übergeben wir die Erstellung von Optionen für Anmeldedaten mit öffentlichem Schlüssel an die Funktion generateRegistrationOptions
.
import {
generateRegistrationOptions,
verifyRegistrationResponse,
generateAuthenticationOptions,
verifyAuthenticationResponse
} from '@simplewebauthn/server';
import { isoBase64URL } from '@simplewebauthn/server/helpers';
router.post('/registerRequest', csrfCheck, sessionCheck, async (req, res) => {
const { user } = res.locals;
// Ensure you nest verification function calls in try/catch blocks.
// If something fails, throw an error with a descriptive error message.
// Return that message with an appropriate error code to the client.
try {
// `excludeCredentials` prevents users from re-registering existing
// credentials for a given passkey provider
const excludeCredentials = [];
const credentials = Credentials.findByUserId(user.id);
if (credentials.length > 0) {
for (const cred of credentials) {
excludeCredentials.push({
id: isoBase64URL.toBuffer(cred.id),
type: 'public-key',
transports: cred.transports,
});
}
}
// Generate registration options for WebAuthn create
const options = await generateRegistrationOptions({
rpName: process.env.RP_NAME,
rpID: process.env.HOSTNAME,
userID: user.id,
userName: user.username,
userDisplayName: user.displayName || '',
attestationType: 'none',
excludeCredentials,
authenticatorSelection: {
authenticatorAttachment: 'platform',
requireResidentKey: true
},
});
// Keep the challenge in the session
req.session.challenge = options.challenge;
return res.json(options);
} catch (e) {
console.error(e);
return res.status(400).send({ error: e.message });
}
});
Öffentlichen Schlüssel speichern

navigator.credentials.create
gibt ein PublicKeyCredential
-Objekt zurück.Wenn navigator.credentials.create
auf dem Client erfolgreich aufgelöst wird, bedeutet das, dass ein Passkey erfolgreich erstellt wurde. Es wird ein PublicKeyCredential
-Objekt zurückgegeben.
Das PublicKeyCredential
-Objekt enthält ein AuthenticatorAttestationResponse
-Objekt, das die Antwort des Passkey-Anbieters auf die Anweisung des Clients zum Erstellen eines Passkeys darstellt. Es enthält Informationen zu den neuen Anmeldedaten, die Sie als RP benötigen, um den Nutzer später zu authentifizieren. Weitere Informationen zu AuthenticatorAttestationResponse
finden Sie im Anhang: AuthenticatorAttestationResponse
.
Senden Sie das Objekt PublicKeyCredential
an den Server. Sobald Sie sie erhalten haben, bestätigen Sie sie.
Übergeben Sie diesen Bestätigungsschritt an Ihre serverseitige FIDO-Bibliothek. Normalerweise wird dafür eine Hilfsfunktion angeboten. SimpleWebAuthn bietet beispielsweise verifyRegistrationResponse
. Weitere Informationen dazu, was im Hintergrund passiert
Speichern Sie nach erfolgreicher Bestätigung die Anmeldedaten in Ihrer Datenbank, damit sich der Nutzer später mit dem zugehörigen Passkey authentifizieren kann.
Verwenden Sie eine separate Tabelle für Public-Key-Anmeldedaten, die mit Passkeys verknüpft sind. Ein Nutzer kann nur ein Passwort, aber mehrere Passkeys haben, z. B. einen Passkey, der über den Apple iCloud-Schlüsselbund synchronisiert wurde, und einen über den Google Passwortmanager.
Hier ist ein Beispielschema, das Sie zum Speichern von Anmeldedaten verwenden können:
- Tabelle Users (Nutzer):
user_id
: Die primäre Nutzer-ID. Eine zufällige, eindeutige, permanente ID für den Nutzer. Verwenden Sie diese als Primärschlüssel für die Tabelle Users (Nutzer).username
: Ein benutzerdefinierter Nutzername, der möglicherweise bearbeitet werden kann.passkey_user_id
: Die passkeyspezifische, PII-kostenlose Nutzer-ID, die in Ihren Registrierungsoptionen durchuser.id
dargestellt wird. Wenn der Nutzer sich später authentifizieren möchte, stellt der Authentifikatorpasskey_user_id
in seiner Authentifizierungsantwort inuserHandle
zur Verfügung. Wir empfehlen,passkey_user_id
nicht als Primärschlüssel festzulegen. Primärschlüssel werden in Systemen häufig verwendet und sind daher in der Regel personenbezogene Daten.
- Tabelle Anmeldedaten mit öffentlichem Schlüssel:
id
: Anmeldedaten-ID. Verwenden Sie diesen Wert als Primärschlüssel für die Tabelle Anmeldedaten mit öffentlichem Schlüssel.public_key
: Öffentlicher Schlüssel der Anmeldedaten.passkey_user_id
: Verwenden Sie diese Spalte als Fremdschlüssel, um eine Verknüpfung mit der Tabelle Users herzustellen.backed_up
: Ein Passkey wird gesichert, wenn er vom Passkey-Anbieter synchronisiert wird. Das Speichern des Sicherungsstatus ist nützlich, wenn Sie in Zukunft für Nutzer mitbacked_up
Passkeys auf Passwörter verzichten möchten. Sie können prüfen, ob der Passkey gesichert ist, indem Sie das BE-Flag inauthenticatorData
untersuchen oder eine serverseitige FIDO-Bibliotheksfunktion verwenden, die in der Regel verfügbar ist, um Ihnen einfachen Zugriff auf diese Informationen zu ermöglichen. Das Speichern der Sicherungsberechtigung kann hilfreich sein, um auf potenzielle Nutzeranfragen zu reagieren.name
: Optionaler Anzeigename für die Anmeldedaten, damit Nutzer ihnen benutzerdefinierte Namen geben können.transports
: Ein Array von Transports. Das Speichern von Transporten ist für die Authentifizierung nützlich. Wenn Transporte verfügbar sind, kann sich der Browser entsprechend verhalten und eine Benutzeroberfläche anzeigen, die dem Transport entspricht, den der Passkey-Anbieter für die Kommunikation mit Clients verwendet, insbesondere für die Re-Authentifizierung, bei derallowCredentials
nicht leer ist.
Andere Informationen können für die Nutzerfreundlichkeit hilfreich sein, z. B. der Passkey-Anbieter, die Erstellungszeit der Anmeldedaten und die letzte Nutzungszeit. Weitere Informationen finden Sie unter Design der Passkey-Benutzeroberfläche.
Beispielcode: Anmeldedaten speichern
In unseren Beispielen verwenden wir die SimpleWebAuthn-Bibliothek.
Hier übergeben wir die Überprüfung der Registrierungsantwort an die Funktion verifyRegistrationResponse
.
import { isoBase64URL } from '@simplewebauthn/server/helpers';
router.post('/registerResponse', csrfCheck, sessionCheck, async (req, res) => {
const expectedChallenge = req.session.challenge;
const expectedOrigin = getOrigin(req.get('User-Agent'));
const expectedRPID = process.env.HOSTNAME;
const response = req.body;
// This sample code is for registering a passkey for an existing,
// signed-in user
// Ensure you nest verification function calls in try/catch blocks.
// If something fails, throw an error with a descriptive error message.
// Return that message with an appropriate error code to the client.
try {
// Verify the credential
const { verified, registrationInfo } = await verifyRegistrationResponse({
response,
expectedChallenge,
expectedOrigin,
expectedRPID,
requireUserVerification: false,
});
if (!verified) {
throw new Error('Verification failed.');
}
const {
aaguid,
credentialPublicKey,
credentialID,
credentialBackedUp
} = registrationInfo;
// Name the credential based on AAGUID
const name =
aaguid === undefined ||
aaguid === '000000-0000-0000-0000-00000000' ?
req.useragent?.platform : aaguids[aaguid].name;
const base64CredentialID = isoBase64URL.fromBuffer(credentialID);
const base64PublicKey = isoBase64URL.fromBuffer(credentialPublicKey);
// Existing, signed-in user
const { user } = res.locals;
// Save the credential
await Credentials.update({
id: base64CredentialID,
passkey_user_id: user.passkey_user_id,
publicKey: base64PublicKey,
name,
aaguid,
transports: response.response.transports,
backed_up: credentialBackedUp,
registered_at: new Date().getTime()
});
// Kill the challenge for this session
delete req.session.challenge;
return res.json(user);
} catch (e) {
delete req.session.challenge;
console.error(e);
return res.status(400).send({ error: e.message });
}
});
Anhang: AuthenticatorAttestationResponse
AuthenticatorAttestationResponse
enthält zwei wichtige Objekte:
response.clientDataJSON
ist eine JSON-Version von Clientdaten, die im Web Daten sind, wie sie vom Browser gesehen werden. Sie enthält den Ursprung des RP, die Challenge undandroidPackageName
, wenn der Client eine Android-App ist. Als RP haben Sie durch das Lesen vonclientDataJSON
Zugriff auf Informationen, die der Browser zum Zeitpunkt dercreate
-Anfrage gesehen hat.response.attestationObject
enthält zwei Informationen:attestationStatement
, was nur relevant ist, wenn Sie die Geräteattestierung verwenden.authenticatorData
sind Daten, wie sie vom Passkey-Anbieter gesehen werden. Als RP haben Sie durch das Lesen vonauthenticatorData
Zugriff auf die Daten, die vom Passkey-Anbieter gesehen und zum Zeitpunkt dercreate
-Anfrage zurückgegeben werden.
authenticatorData
enthält wichtige Informationen zur Anmeldedaten für den öffentlichen Schlüssel, die mit dem neu erstellten Passkey verknüpft sind:
- Die Anmeldedaten mit öffentlichem Schlüssel selbst und eine eindeutige Anmeldedaten-ID dafür.
- Die RP‑ID, die mit den Anmeldedaten verknüpft ist.
- Flags, die den Nutzerstatus bei der Erstellung des Passkeys beschreiben: ob ein Nutzer tatsächlich anwesend war und ob der Nutzer erfolgreich bestätigt wurde (siehe userVerification deep dive).
- Die AAGUID ist eine Kennung für den Passkey-Anbieter, z. B. den Google Passwortmanager. Anhand der AAGUID können Sie den Passkey-Anbieter identifizieren und den Namen auf einer Seite zur Passkey-Verwaltung anzeigen. (siehe Passkey-Anbieter mit AAGUID ermitteln)
Auch wenn authenticatorData
in attestationObject
verschachtelt ist, sind die darin enthaltenen Informationen für die Passkey-Implementierung erforderlich, unabhängig davon, ob Sie die Attestierung verwenden. authenticatorData
ist codiert und enthält Felder, die in einem Binärformat codiert sind. Das Parsen und Decodieren wird in der Regel von Ihrer serverseitigen Bibliothek übernommen. Wenn Sie keine serverseitige Bibliothek verwenden, sollten Sie getAuthenticatorData()
clientseitig nutzen, um sich serverseitig etwas Parsing- und Decodierungsarbeit zu sparen.
Anhang: Bestätigung der Registrierungsantwort
Die Überprüfung der Registrierungsantwort umfasst die folgenden Prüfungen:
- Die RP‑ID muss mit Ihrer Website übereinstimmen.
- Prüfen Sie, ob der Ursprung der Anfrage ein erwarteter Ursprung für Ihre Website ist (Hauptwebsite-URL, Android-App).
- Wenn Sie eine Nutzerbestätigung benötigen, muss das Flag
authenticatorData.uv
auftrue
gesetzt sein. - Das Flag für die Nutzeranwesenheit
authenticatorData.up
sollte normalerweisetrue
sein. Wenn die Anmeldedaten jedoch bedingt erstellt werden, sollte esfalse
sein. - Prüfen Sie, ob der Client die von Ihnen gestellte Aufgabe lösen konnte. Wenn Sie die Gerätebestätigung nicht verwenden, ist diese Prüfung nicht wichtig. Die Implementierung dieser Prüfung ist jedoch eine Best Practice, da sie sicherstellt, dass Ihr Code bereit ist, falls Sie sich in Zukunft für die Verwendung der Geräteattestierung entscheiden.
- Achten Sie darauf, dass die Anmeldedaten-ID noch nicht für einen Nutzer registriert ist.
- Prüfen Sie, ob der Algorithmus, der vom Passkey-Anbieter zum Erstellen der Anmeldedaten verwendet wird, ein Algorithmus ist, den Sie in jedem
alg
-Feld vonpublicKeyCredentialCreationOptions.pubKeyCredParams
aufgeführt haben.publicKeyCredentialCreationOptions.pubKeyCredParams
wird in der Regel in Ihrer serverseitigen Bibliothek definiert und ist für Sie nicht sichtbar. So wird sichergestellt, dass sich Nutzer nur mit Algorithmen registrieren können, die Sie zugelassen haben.
Weitere Informationen finden Sie im Quellcode von SimpleWebAuthn für verifyRegistrationResponse
oder in der vollständigen Liste der Bestätigungen in der Spezifikation.