Skip to main content

POST /v1/users/me/passkeys/register/complete

Finalize a WebAuthn enrollment ceremony. Persists the new credential and flips passkey_enabled = true on the user record.

::: tip Auth Required: cookie or Bearer. :::

Request

POST /v1/users/me/passkeys/register/complete

HeaderRequiredNotes
Cookie: identsphere_at=... OR Authorization: Bearer ...yes
Content-Type: application/jsonyes

Body

{
"credential": { /* PublicKeyCredential from navigator.credentials.create() */ },
"label": "MacBook Touch ID"
}
FieldTypeRequiredNotes
credentialobjectyesThe browser-built PublicKeyCredential response. Pass through unmodified.
labelstring | nullnoUser-friendly label. Shown in the passkey list UI.

Response

200 OK

{
"id": "a8f3c2d1-...",
"label": "MacBook Touch ID",
"aaguid": null,
"transport": null,
"created_at": "2026-05-28T12:00:00+00:00",
"last_used_at": null
}

Error responses

StatusCodeWhen
400invalid_inputNo matching pending registration challenge for this user, OR the credential failed WebAuthn validation.
401authentication_requiredNo valid auth credential.
404not_foundUser no longer exists.
409conflictThis credential is already registered (likely to a different user).
500internal_errorSerialization or DB failure.

Notes

  • The challenge row in passkey_challenges is deleted regardless of whether the ceremony succeeded — defeats replay attempts.
  • The full Passkey struct (COSE public key, sign counter, flags) is serialized to JSON, base64-encoded, and stored in user_passkeys.public_key.
  • An audit entry (auth.passkey.added) is recorded.