In diesem Leitfaden wird beschrieben, wie Sie Nutzern ermöglichen, ihre registrierten Passkeys anzusehen, umzubenennen und zu löschen. Gleichzeitig werden gespeicherte Anmeldedaten und Nutzerprofildetails zwischen dem Server und den Passwortmanagern des Nutzers mithilfe der Signal API synchronisiert.
Serverseitige Vorgänge
Ihre Backend-Datenbankebene und -Endpunkte MÜSSEN gängige CRUD-Aktionen für registrierte Anmeldedaten sowie die Aktualisierung von Nutzerprofildetails unterstützen. Der Server ist von frameworkspezifischen Bibliotheken entkoppelt und stellt Endpunkte für Folgendes bereit:
- Alle Nutzeranmeldedaten auflisten: Alle
StoredPasskeyCredential-Einträge abrufen, die mit der ID des angemeldeten Nutzers übereinstimmen. - Anmeldedatennamen aktualisieren: Akzeptieren Sie einen neuen benutzerdefinierten Alias für eine bestimmte Anmeldedaten-ID und speichern Sie die Aktualisierung auf dem Server.
- Anmeldedaten löschen: Entfernen Sie eine bestimmte Anmeldedaten-ID aus der Datenbank.
- Nutzerkontodetails aktualisieren: Akzeptieren Sie die aktualisierten Werte für
name(Nutzername) unddisplayNamefür das Profil des angemeldeten Nutzers, damit der Client sie mit dem Passkey-Anbieter synchronisieren kann.
// Node.js routing example for credential CRUD and user profile rename
router.get('/api/credentials', checkUserAuthenticated, async (req, res) => {
const list = await db.findCredentialsByUserId(req.user.id);
return res.json(list);
});
router.put('/api/credential/:id', checkUserAuthenticated, async (req, res) => {
const { id } = req.params;
const { name } = req.body;
const cred = await db.findCredentialById(id);
if (!cred || cred.passkeyUserId !== req.user.id) {
return res.status(404).json({ error: 'Credential not found.' });
}
cred.name = name;
await db.saveCredential(cred);
return res.json(cred);
});
router.delete('/api/credential/:id', checkUserAuthenticated, async (req, res) => {
const { id } = req.params;
const cred = await db.findCredentialById(id);
if (!cred || cred.passkeyUserId !== req.user.id) {
return res.status(404).json({ error: 'Credential not found.' });
}
await db.deleteCredential(id);
return res.json({ success: true });
});
router.put('/api/user', checkUserAuthenticated, async (req, res) => {
const { name, displayName } = req.body;
const updatedUser = await db.updateUser(req.user.id, { name, displayName });
return res.json(updatedUser);
});
Clientseitige Verwaltungsoberfläche
Rendern Sie einen speziellen Bereich für Einstellungen, in dem Nutzer ihre registrierten Authentifizierungsoptionen und Kontoprofildetails einfach prüfen und verwalten können:
- Gespeicherte Liste anzeigen: Ruft die Liste von deinem Endpunkt ab und rendert einzelne Zeilen mit Anmeldedaten. Wenn die Antwort leer ist, rendern Sie eine hilfreiche Meldung, z.B. „Keine Passkeys gefunden“.
- AAGUID-Metadaten zuordnen: Suchen Sie für jeden Passkey die zugehörige
aaguid-Eigenschaft in Ihrer lokalen Registry, um die Anbieterdetails zu rendern. Weitere Informationen finden Sie im Abschnitt Passkey-Anbieter anhand der AAGUID ermitteln. - Anforderungen an die Benutzeroberfläche für einzelne Elemente: Jede Zeile im Listencontainer MUSS Folgendes enthalten:
- Anbietersymbol: Von der AAGUID abgeleitetes Bild oder Daten-URI.
- Anbieter/Benutzerdefinierter Name: Von der AAGUID abgeleiteter Name oder vom Nutzer umbenannter String.
- Registrierungsdatum: Der in der Datenbank gespeicherte Rohzeitstempel
registeredAt, der für die Anzeige beim Kunden in ein menschenlesbares Datum formatiert wurde. - Datum der letzten Verwendung: Der in der Datenbank gespeicherte Roh-Zeitstempel
lastUsedAt, der für die Anzeige im Client in ein menschenlesbares Datum (falls vorhanden) formatiert wurde. - Schaltfläche „Umbenennen“: Löst eine Umbenennungsaktion aus, um den benutzerdefinierten Alias der Anmeldedaten auf dem Server (
PUT /api/credential/:id) zu aktualisieren. Hinweis: Das Umbenennen des Alias eines einzelnen Passkeys ist eine serverseitige Labeländerung und ruftsignalCurrentUserDetails()NICHT auf. - Schaltfläche „Löschen“: Löst das Löschen des Ausweises (
DELETE /api/credential/:id) aus und synchronisiert die verbleibenden Ausweis-IDs übersignalAllAcceptedCredentials().
- Benutzeroberfläche zum Umbenennen von Nutzerkonten:
- Stellen Sie ein Steuerelement bereit, mit dem der Nutzer den Nutzernamen seines Kontos (
name) oderdisplayName(PUT /api/user) aktualisieren kann, und synchronisieren Sie die aktualisierten Nutzerdetails sofort überPublicKeyCredential.signalCurrentUserDetails()mit dem Passwortmanager.
- Stellen Sie ein Steuerelement bereit, mit dem der Nutzer den Nutzernamen seines Kontos (
- Bedingte Schaltfläche „Passkey erstellen“:
- Bieten Sie auf der Verwaltungsseite eine gut sichtbare Schaltfläche zum Erstellen eines Passkeys an. Bevor dieses UI-Element gerendert wird, MUSS die Seite die Funktionen mit
PublicKeyCredential.getClientCapabilities()erkennen, um zu prüfen, ob der Plattform-Authenticator unterstützt wird. Wenn Passkeys nicht unterstützt werden, blenden Sie diese Schaltfläche aus und empfehlen Sie stattdessen die Standard-MFA-Registrierung. - Registrierung eines Sicherheitsschlüssels zulassen, indem
authenticatorSelection.authenticatorAttachmentbeim Aufruf vonnavigator.credentials.create()weggelassen wird.
- Bieten Sie auf der Verwaltungsseite eine gut sichtbare Schaltfläche zum Erstellen eines Passkeys an. Bevor dieses UI-Element gerendert wird, MUSS die Seite die Funktionen mit
Signal API-Synchronisierung
Mit der Signal API kann die Anwendung den Status von Anmeldedaten an Passwortmanager senden, sodass die synchronisierten Vaults des Nutzers und Ihre Backend-Datenbank immer auf dem gleichen Stand sind.
- Regel für die Featureerkennung und Parametercodierung:
- Prüfen Sie immer, ob die Signal API-Methoden unterstützt werden, bevor Sie sie aufrufen (
if (PublicKeyCredential.signalAllAcceptedCredentials)undif (PublicKeyCredential.signalCurrentUserDetails)). - Alle
userId- und Anmeldedaten-ID-Parameter, die an Signal API-Methoden (signalAllAcceptedCredentials,signalCurrentUserDetails) übergeben werden, MÜSSEN Base64URL-codierte Strings sein.
- Prüfen Sie immer, ob die Signal API-Methoden unterstützt werden, bevor Sie sie aufrufen (
- Synchronisierung beim Seitenaufbau initiieren:
- Die Anwendung MUSS
signalAllAcceptedCredentials()automatisch aufrufen, wenn die Verwaltungsseite geladen wird (z. B. in einemDOMContentLoaded-Event-Listener, bei der Modulinitialisierung oder in einem Framework-Komponenten-Mount-Hook wieuseEffect).
- Die Anwendung MUSS
- Synchronisierung von Verwaltungsupdates:
- Die Anwendung MUSS
signalAllAcceptedCredentials()sofort im Click-Handler zum Löschen von Anmeldedaten aufrufen, nachdem die Anmeldedaten abgerufen wurden. - Die Anwendung MUSS
signalCurrentUserDetails()sofort im Click-Handler für die Umbenennung des Nutzerkontos (nameoderdisplayName) nach dem Abruf aufrufen.
- Die Anwendung MUSS
// Client-side management synchronization ES module
import { listFetch, renameCredentialFetch, updateUserFetch, deleteFetch } from './api.js';
// Base64URL-encoded User ID string (illustration only)
const base64UrlUserId = "M2YPl-KGnA8";
async function syncAcceptedCredentials(currentCredentialsList) {
if (!window.PublicKeyCredential || !PublicKeyCredential.signalAllAcceptedCredentials) return;
try {
const credentialIds = currentCredentialsList.map(c => c.id); // Array of Base64URL credential ID strings
await PublicKeyCredential.signalAllAcceptedCredentials({
rpId, // RP ID must match the one defined on the server
userId: base64UrlUserId, // User ID Base64URL-encoded string
allAcceptedCredentialIds: credentialIds
});
} catch (e) {
console.error('SignalAllAcceptedCredentials sync failure:', e);
}
}
async function loadManagementPanel() {
const response = await listFetch();
const list = await response.json();
renderUI(list);
// Sync accepted credentials on initial load
await syncAcceptedCredentials(list);
}
async function performDelete(credentialId) {
const response = await deleteFetch(credentialId);
if (response.ok) {
const updatedResponse = await listFetch();
const updatedList = await updatedResponse.json();
renderUI(updatedList);
// Sync remaining accepted credentials after deletion
await syncAcceptedCredentials(updatedList);
}
}
// Renaming a passkey's nickname updates the server record only
async function performCredentialRename(credentialId, newCredentialName) {
const response = await renameCredentialFetch(credentialId, { name: newCredentialName });
if (response.ok) {
const updatedResponse = await listFetch();
renderUI(await updatedResponse.json());
}
}
// Renaming the user's account username or displayName triggers signalCurrentUserDetails
async function performUserRename(rpId, userId, updatedName, updatedDisplayName) {
const response = await updateUserFetch({ name: updatedName, displayName: updatedDisplayName });
if (response.ok && window.PublicKeyCredential && PublicKeyCredential.signalCurrentUserDetails) {
try {
await PublicKeyCredential.signalCurrentUserDetails({
rpId, // RP ID must match the one defined on the server
userId, // Base64URL-encoded user ID
name: updatedName, // Updated username
displayName: updatedDisplayName // Updated display name
});
} catch (e) {
console.error('SignalCurrentUserDetails sync failure:', e);
}
}
}
window.addEventListener('DOMContentLoaded', loadManagementPanel);
Passkey-Anbieter anhand der AAGUID ermitteln
Eine AAGUID (Authenticator Attestation Globally Unique Identifier) ist eine 128-Bit-Kennung, die das Modell des Authentifikators darstellt, nicht eine bestimmte Instanz. Sie ist bei der Passkey-Registrierung in den Authenticator-Daten enthalten und kann verwendet werden, um zu bestimmen, welcher Passkey-Anbieter (z.B. Google Passwortmanager, iCloud-Schlüsselbund, 1Password) die Anmeldedaten erstellt hat.
Die AAGUID sollte nur verwendet werden, um Nutzern bei der Verwaltung von Passkeys zu helfen. Sie kann geändert werden, sofern sie nicht kryptografisch bestätigt ist. Das wird von Plattform-Passkeys derzeit nicht unterstützt.
1. AAGUID-Registry
Eine von der Community gepflegte JSON-Zuordnung von AAGUIDs zu Anbieternamen und ‑symbolen ist hier verfügbar:
https://raw.githubusercontent.com/passkeydeveloper/passkey-authenticator-aaguids/refs/heads/main/combined_aaguid.json
Jeder Eintrag hat das folgende Schema:
{
"<aaguid-uuid>": {
"name": "Provider Name",
"icon_light": "data:image/png;base64,...",
"icon_dark": "data:image/png;base64,..."
}
}
2. AAGUID nach der Registrierung verwenden
Nachdem Sie eine Registrierungsantwort bestätigt haben, lesen Sie die aaguid aus dem Registrierungsergebnis und suchen Sie sie in der Registry, um die name und providerIcon der Anmeldedaten zu füllen:
Prüfen Sie vor dem Suchen der AAGUID in der Registry, ob sie '00000000-0000-0000-0000-000000000000' entspricht. Wenn ja, überspringen Sie die Registrierungssuche und legen Sie name auf einen Fallback fest (z.B. Gerätename aus dem User-Agent oder „Unbekannter Passkey-Anbieter“) und providerIcon auf undefined. Suchen Sie nur nach AAGUIDs, die nicht null sind.
import aaguids from './aaguids.json' with { type: 'json' };
const { aaguid } = registrationInfo;
if (aaguid === '00000000-0000-0000-0000-000000000000') {
// use the device name as the passkey provider based on
// the information derived from the user agent string,
// or just say "Unknown passkey provider"
} else {
const provider = aaguids[aaguid];
const credential = {
// ...other fields
aaguid,
name: provider?.name || 'Unknown passkey provider',
providerIcon: provider?.icon_light,
};
}
Fallback-Strategien
Fallback für die Passkey-Funktionserkennung
Installieren Sie immer „webauthn-polyfills“ und importieren Sie es in den Kontext.
Solange PublicKeyCredential unterstützt wird, wird auch PublicKeyCredential.getClientCapabilities unterstützt.
import 'webauthn-polyfills';
Signal API-Synchronisierungs-Fallback
Wenn der Browser PublicKeyCredential.parseRequestOptionsFromJSON nicht unterstützt, verwenden Sie die „webauthn-polyfills“:
<script type="module">
if (!PublicKeyCredential.parseRequestOptionsFromJSON) {
await import('https://unpkg.com/webauthn-polyfills');
}
</script>
Dadurch wird auch die Unterstützung für PublicKeyCredential.prototype.toJSON hinzugefügt.