Registration flow
A self-serve user goes through this once, right after signing up.- Sign up with an email and password.
- Confirm the email code we send them. This returns a
SECURITY_SETUP_REQUIREDchallenge, not tokens. If the code didn’t arrive or expired, resend it. - Set up security: register a passkey or turn on 2FA. This is the step that gets tokens.
Login flow
An existing user logs in one of three ways.Login with passkey (recommended)
Passkey sign-in skips challenges entirely. Two calls, with the device’s passkey ceremony in between:- Start a passkey login: you get a
session_tokenandfido2optionsto pass to the device. No bearer token needed. - Unlock the passkey on the device. Pass
fido2optionsto the platform’s credential API (navigator.credentials.get()on the web, or the native equivalent on iOS and Android). The OS prompts the user for Face ID, Touch ID, or their device PIN, then hands back a signed assertion. Nothing is sent to us during this step. - Finish a passkey login: send the
session_tokenand the assertion. This returns tokens.
Login with password
Password login is one call plus whatever the challenge asks for. The login call itself never returns tokens.-
Log in with the email and password. You get back a
challengeand, for most challenges, asession_token. -
Do what the challenge says. There are three:
-
If step 2 returned
SECURITY_SETUP_REQUIREDorNEW_DEVICE_SECURITY_OPTIONS, set up security: a passkey or 2FA. That’s the call that finally returns tokens.
- Forgot the password? Start a password reset emails a code, then finish the reset with the code and the new password. Finishing also logs the user in.
- Adding 2FA to an account that’s already signed in: start 2FA setup to get the QR code, then confirm it with the first code from the authenticator app.
Login with SSO
If your program uses your own identity provider, the user signs in there instead and none of the endpoints above apply. See Single sign-on. Everything after sign-in, including the tokens below, is the same.Using your tokens
A successful login returns these tokens:
Treat all three as credentials. On the web, keep them in an
httpOnly cookie or in memory,
never localStorage. On mobile, use the Keychain or Keystore. Refresh shortly before
expires_in runs out, clear all three on log out, and never write a token to a log.