Skip to content

Associating passkeys with local user accounts

A successful passkey registration or authentication returns a Principal or ExtendedPrincipal. The two account-linking fields you will use most often are:

  • userId: Application user ID supplied by your backend during registration
  • authenticatorId: Passkey ID
{
"authenticatorId": "spy28n0bqca11tq",
"userId": "myInternalUserId"
}

Set your user ID during authorizePasskeyRegistration:

backend/register.ts
const result = await passlock.authorizePasskeyRegistration({
userId: user.id,
username: user.email,
});

When you later exchange the registration code, confirm the returned userId matches the local user that started registration:

backend/register.ts
const result = await passlock.exchangeCode({ code });
if (!result.success) {
throw new Error(result.error.message);
}
if (result.value.userId !== user.id) {
throw new Error("Passkey registration user mismatch");
}

You should still store authenticatorId for passkey management, deletion, audit logs, duplicate-prevention queries and support workflows. A single local user can have multiple passkeys, so your local data model will often track both fields.

---
title: Example table structure
---
erDiagram
  user[user] {
    string id PK
  }
  authenticator[authenticator] {
    string authenticatorId PK "Passlock passkey ID"
    string userId FK "points to user.id"
  }
  user ||--|{ authenticator : "User has one or more passkeys"

A passkey’s userId is immutable. If a passkey is linked to the wrong local account, delete that passkey and register another with the correct user ID.