PasskeyBase

Passkey management

Real users have more than one device. PasskeyBase lets each user enroll, name, and remove multiple passkeys and security keys independently. There are three ways to expose this to your users — pick the one that fits your integration.

Three integration paths

  • Hosted account page — zero integration. Redirect users to a branded page on your tenant domain.
  • JavaScript / native SDK — build your own settings UI callinglistPasskeys(), renamePasskey(), removePasskey().
  • Management API — server-to-server endpoints for admin and support flows.

Option 1: Hosted account page

The fastest integration. Every PasskeyBase tenant exposes a hosted key-management page athttps://{tenant}.auth.ing/account/keys. Send users there and they can manage their own passkeys without you writing any code.

Hosted page redirect
// The simplest integration: redirect users to the hosted account page.
<a href="https://myapp.auth.ing/account/keys?return_to=/settings">
  Manage your passkeys
</a>

// Users land on a branded page where they can:
// - See all enrolled passkeys with names + last-used time
// - Rename a passkey
// - Remove a passkey
// - Enroll a new passkey or security key
// - Return to your app via the return_to URL

The page is rendered by Express using your tenant's branding (logo, primary color). It requires the user to have an active session — if they're not signed in, they'll be sent through the standard passkey login flow first.

Option 2: SDK methods

List passkeys

Returns all passkeys enrolled for the currently signed-in user. Use this to render a settings UI in your own app.

listPasskeys()
import { PasskeyBase } from '@passkeybase/js';

const pb = new PasskeyBase({
  clientId: 'pk_live_abc123',
  domain: 'myapp.auth.ing',
});

// List all passkeys for the currently signed-in user
const keys = await pb.listPasskeys();

// Returns:
// [
//   {
//     id: 'cred_01HZ...',
//     name: 'iPhone',
//     type: 'platform',          // 'platform' | 'roaming' (security key)
//     createdAt: '2026-01-15T...',
//     lastUsedAt: '2026-04-29T...',
//     transports: ['internal', 'hybrid'],
//   },
//   {
//     id: 'cred_01J0...',
//     name: 'YubiKey 5C',
//     type: 'roaming',
//     createdAt: '2026-02-03T...',
//     lastUsedAt: '2026-04-12T...',
//     transports: ['usb', 'nfc'],
//   }
// ]

Rename a passkey

Update the user-visible label of a passkey.

renamePasskey()
// Rename a passkey
await pb.renamePasskey('cred_01HZ...', 'Work laptop');

Remove a passkey

Revoke a credential. The next time the user tries to authenticate with that key, the server will reject it.

removePasskey()
// Remove a passkey
await pb.removePasskey('cred_01HZ...');

// Note: PasskeyBase will refuse to remove the user's last
// passkey unless { allowLastKey: true } is passed, to prevent
// users from accidentally locking themselves out.
await pb.removePasskey('cred_01HZ...', { allowLastKey: true });

Enroll an additional passkey

Add a new key to the currently signed-in user's account — for example, when adding a second device or a YubiKey. Unlike enrollPasskey(), which is for users coming from an existing auth system, this method requires an active PasskeyBase session.

enrollAdditionalPasskey()
// Enroll an additional passkey for the currently signed-in user.
// Unlike enrollPasskey() (which uses an external enrollment token),
// this works once the user is already signed in via PasskeyBase.
const result = await pb.enrollAdditionalPasskey({
  name: 'YubiKey 5C',                    // optional initial label
  authenticator: 'cross-platform',       // 'platform' | 'cross-platform' | 'any'
});

Option 3: Management API

Server-to-server endpoints for cases where the user is not present — admin tooling, customer-support workflows, automated revocation, etc. Calls authenticate with a Management API token (see API reference).

List a user's credentials

GET /api/v1/users/:user_id/credentials
// GET https://myapp.auth.ing/api/v1/users/{user_id}/credentials
// Authorization: Bearer <management_api_token>

const response = await fetch(
  `https://myapp.auth.ing/api/v1/users/${userId}/credentials`,
  { headers: { Authorization: `Bearer ${managementToken}` } }
);

const { credentials } = await response.json();
// [
//   {
//     id: 'cred_01HZ...',
//     name: 'iPhone',
//     type: 'platform',
//     created_at: '2026-01-15T...',
//     last_used_at: '2026-04-29T...',
//     transports: ['internal', 'hybrid']
//   },
//   ...
// ]

Revoke a credential

DELETE /api/v1/credentials/:id
// DELETE https://myapp.auth.ing/api/v1/credentials/{credential_id}
// Authorization: Bearer <management_api_token>

await fetch(
  `https://myapp.auth.ing/api/v1/credentials/${credentialId}`,
  {
    method: 'DELETE',
    headers: { Authorization: `Bearer ${managementToken}` },
  }
);

Platform credentials vs roaming authenticators

PasskeyBase distinguishes two credential types in all three surfaces:

  • platform — bound to a specific device (Face ID, Touch ID, Windows Hello, Android biometrics). May be synced across the user's other devices via iCloud Keychain or Google Password Manager.
  • roaming — an external security key like a YubiKey, Solo, or Feitian. Can move between devices via USB, NFC, or Bluetooth.

The transports array hints at how the credential can be used (internal, usb, nfc, ble,hybrid). Most apps don't need to differentiate — list and label everything the user has — but the data is there if you do.

Lockout protection

By default, the SDK and the hosted account page will refuse to remove a user's last remaining passkey, to prevent accidental account lockouts. Pass{ allowLastKey: true } to override (for example, if you've already confirmed the user has another way to recover their account). The Management API does not enforce this — server-side admin flows are expected to handle their own safeguards.

Mobile

The iOS and Android SDKs expose the same methods —listPasskeys(), renamePasskey(), removePasskey(),enrollAdditionalPasskey() — adapted to platform conventions (async throws on Swift, suspend fun on Kotlin). See theSwift and Kotlin SDK references for platform-specific signatures.