Auth API
buildingWebAuthn sign-in, bound to your domain rather than ours — and what that constrains.
Passkeys
Sign in with a fingerprint, a face, or a security key. No password to phish.
Passkeys are bound to your domain, not ours
That shapes the whole setup. A browser only accepts a credential whose relying party is your own site, so we derive it from the first browser origin you register:
{ "rp": { "name": "Acme", "id": "app.acme.com" } }The consequence to know before you build on it: a passkey works on the domain it was created for and nowhere else. A project serving two unrelated domains can have passkeys on one of them. That is WebAuthn's rule rather than ours, and there is no way around it — the browser enforces it, not our server.
A project with no registered origins cannot use passkeys at all, which is already true of every other browser flow.
The flow
Standard WebAuthn. The browser's own navigator.credentials does the hard part;
these three calls are the server half. All take a publishable key.
GET /v1/auth/passkey/generate-register-options → options carrying your rp
POST /v1/auth/passkey/verify-registration → stores the credential
POST /v1/auth/sign-in/passkey → a sessionSigned in, so the credential attaches to a known account.
1curl "$BASE/v1/auth/passkey/generate-register-options" \2 -H "authorization: Bearer $PK" \3 -b "session cookie"Pass the response to navigator.credentials.create().
Post back what the browser returned. On success the passkey is stored and usable immediately.
Returns a session exactly as a password sign-in does — same cookie, same shape. A passkey satisfies both factors at once, so an account with two-factor enabled is not asked for a code as well.
Errors
| Status | Code | Means |
|---|---|---|
400 | — | No registered origin, so there is no relying party to bind to |
400 | — | The credential was created for a different domain |
401 | — | Unknown or revoked credential |
429 | — | Rate limited; see limits |