Credential management

Listing, renaming and revoking passkeys from an account settings page.

Everything an account settings page needs. All three methods take userId first, and check it.

Methods

listCredentialsmethod

listCredentials(userId: string): Promise<PublicCredentialInfo[]>

Every passkey on an account, shaped for display.

Returns

PublicCredentialInfo[]
const passkeysForUser = await passkeys.listCredentials(req.session.userId);
[
  {
    id: 'kruh1zrhU4HRYwXpxyCsG6tqknL6sIAC81dnWTtYj18',
    nickname: 'MacBook Pro',
    deviceType: 'multiDevice',
    backedUp: true,
    transports: ['internal', 'hybrid'],
    aaguid: '00000000-0000-0000-0000-000000000000',
    createdAt: new Date('2026-02-11T09:14:22.000Z'),
    lastUsedAt: new Date('2026-08-27T05:41:14.523Z'),
  },
]

Why this is a separate shape from PasskeyCredential

renameCredentialmethod

renameCredential(userId: string, credentialId: string, nickname: string): Promise<void>

Give a passkey a human label. Truncated to 128 characters.

await passkeys.renameCredential(req.session.userId, credentialId, 'Work laptop');

Why nicknames are worth offering

Throws

unknown_credential

deleteCredentialmethod

deleteCredential(userId: string, credentialId: string): Promise<void>

Revoke a passkey.

await passkeys.deleteCredential(req.session.userId, credentialId);

Why deleting the last passkey is refused

Throws

unknown_credential
last_credential

A settings page

Server routes, using your existing auth middleware:

app.get('/settings/passkeys', requireAuth, async (req, res) => {
  res.json(await passkeys.listCredentials(req.session.userId));
});
 
app.patch('/settings/passkeys/:id', requireAuth, async (req, res) => {
  await passkeys.renameCredential(req.session.userId, req.params.id, req.body.nickname);
  res.json({ ok: true });
});
 
app.delete('/settings/passkeys/:id', requireAuth, async (req, res) => {
  await passkeys.deleteCredential(req.session.userId, req.params.id);
  res.json({ ok: true });
});

Or mount the adapter with getSessionUserId and get the same three routes for free under /passkey/credentials.

What to surface to the user

deviceType'multiDevice' | 'singleDevice'
backedUpboolean
lastUsedAtDate | undefined
createdAtDate
aaguidstring