Every way a member can sign in and manage their account
Acme Brew is a fictional brand built on one identity platform. Every tab below is a real
flow that platform offers an end user, each demonstrated live against — our own branded domain, not a third-party page. No
tab guesses which methods are turned on: sign-in tabs land you on the real hosted page,
which decides for itself, on the spot, which buttons to show.
Password
The default way to sign in: an email address and a password, checked on the server and
never seen by this page. Clicking through sends you to our branded sign-in page on , where you type your password directly into the identity
platform — this demo never touches it.
What this exercises
GET https://your-brand.example/.well-known/oauth-authorization-server
→ { authorization_endpoint, token_endpoint, ... } (RFC 8414)
# Browser is redirected to authorization_endpoint with a PKCE challenge,
# then to your hosted sign-in page.
POST token_endpoint (RFC 6749 §4.1.3)
grant_type=authorization_code&code=...&code_verifier=...
→ { access_token, id_token, refresh_token, expires_in }
Magic link
No password at all: type your email, and a one-time sign-in link arrives in your inbox. Clicking it signs you in and returns you straight to whatever you were doing — the same continuation a password sign-in would land on. This is the one flow with no page of its own to visit first, so this tab calls the sign-in API directly with the tenant's own publishable key, the same key the TV app tab uses to start pairing.
Note on hosting this yourself: this is the one tab that calls a publishable-key-authenticated endpoint straight from the browser rather than landing on a hosted page. That endpoint accepts cross-origin requests from any origin, the same "body-authenticated, no cookies" posture the device-flow endpoints use — a page served from your own domain can call it directly with no allow-list to configure.
What this exercises
POST /v1/auth/magic-link
X-API-Key: <publishable key>
{ "email": "[email protected]", "oauth_params": { "client_id": "...", "redirect_uri": "...", ... } }
→ 200 { "message": "If your email is registered, a magic link has been sent",
"resend_after_seconds": 60 }
# Always 200, whether or not the address is registered — the response never reveals which.
# Clicking the emailed link completes the SAME authorization request as any other sign-in.
Email one-time code
Type your email, receive a short numeric code, and enter it on the page — no password, no link to click, useful when checking email is easier than remembering a password. This tab lands you directly on the hosted email-code screen rather than the general sign-in page.
What this exercises
POST /v1/auth/email-otp { "email": "[email protected]" }
→ 200 { "message": "...", "resend_after_seconds": 60 }
POST /v1/auth/email-otp/verify { "email": "[email protected]", "code": "123456" }
→ 200 { "next_step": "authenticated", "access_token", "refresh_token", "expires_in" }
→ 401 { "detail": "..." } (wrong or expired code — deliberately the same opaque
answer as every other cause, so no response tells you which)
Passkey sign-in
A passkey replaces your password with your device's own fingerprint, face, or screen lock — nothing typed, nothing to phish. A "Sign in with a passkey" button appears on the hosted sign-in page only for tenants (and accounts) that have one enrolled; this tab lands you on the same page a password sign-in uses, since that is where the button lives.
What this exercises
# WebAuthn (FIDO2) authentication ceremony, browser ↔ platform, no password
# transmitted at any point. Completes the same authorization request as
# every other sign-in method above.
Manage passkeys
Once signed in, a member can add a passkey for a new device, rename one so multiple devices stay identifiable, or remove one that is lost or retired — all from their own account, no support ticket needed.
Opens in a new tab. Not signed in yet? You'll be sent to sign in first, then straight back here.
What this exercises
GET /v1/users/{id}/passkeys → [{ id, name, created_at, last_used_at }]
POST /v1/passkeys/register-begin → WebAuthn registration ceremony, step 1
POST /v1/passkeys/register-finish → WebAuthn registration ceremony, step 2
PATCH /v1/users/{id}/passkeys/{passkeyId} { "name": "My laptop" }
DELETE /v1/users/{id}/passkeys/{passkeyId}
Two-factor authentication
A member can turn on an authenticator-app code as a second step after their password. Once it's on, a password sign-in pauses for that code before completing — you don't ask for it separately, it simply appears in the sign-in flow when the account needs it. Turning it on (and generating backup recovery codes) happens from account settings.
If the account you sign in with has two-factor turned on, the code prompt appears automatically as part of that same flow.
What this exercises
POST /v1/auth/login { "email": "...", "password": "..." }
→ 200 { "next_step": "mfa_verify", "mfa_challenge_token": "..." }
POST /v1/auth/mfa/verify-login { "mfa_challenge_token": "...", "code": "123456" }
→ 200 { "access_token", "id_token", "refresh_token", "expires_in" }
Device flow (TV app)
For a screen with no keyboard — a smart TV, a set-top box, a CLI — pairing happens by showing a short code that you enter on a phone or computer you already trust. This is its own dedicated page, since it's a different device experience entirely rather than a variation on the sign-in page above.
What this exercises
POST /v1/auth/oauth/device/authorize { "publishable_key": "..." }
→ { device_code, user_code, verification_uri, verification_uri_complete,
expires_in, interval }
POST /v1/auth/oauth/device/token { "device_code", "publishable_key", "grant_type": "..." }
→ 200 { access_token, refresh_token, ... } once approved
→ 400 { "code": "oauth/authorization_pending" } while waiting (RFC 8628)
Sessions & sign-in history
A member can see every device currently signed in, sign one of them out remotely (lost phone, shared computer), and review a history of recent sign-ins, failed attempts, and security events on their own account.
Opens in a new tab. Not signed in yet? You'll be sent to sign in first, then straight back here.
What this exercises
GET /v1/auth/me → { ..., sessions: [{ id, created_at, last_used_at, is_current }] }
GET /v1/auth/me/activity → { data: [{ event_type: "auth.login", created_at, ... }] }
DELETE /v1/auth/me/sessions/{id} → 204
Connected apps & consents
Every time a member approves an app's access request, that approval is recorded and listed here — what was approved, for which app, and when. They can revoke any of them at any time, which immediately ends that app's access.
Opens in a new tab. Not signed in yet? You'll be sent to sign in first, then straight back here.
What this exercises
GET /v1/auth/me/grants → { data: [{ id, app_name, scopes, authorized_at }] }
DELETE /v1/auth/me/grants/{id} → 204
Account settings
Change a password, turn two-factor off, regenerate backup recovery codes, or unlink a connected social account — all self-service, no support ticket.
Opens in a new tab. Not signed in yet? You'll be sent to sign in first, then straight back here.
What this exercises
POST /v1/auth/change-password { current_password, new_password }
DELETE /v1/auth/mfa
POST /v1/auth/mfa/recovery-codes → { recovery_codes: [...] }
GET /v1/users/me/link → { data: [{ provider, linked_at }] }
DELETE /v1/users/me/link/{provider}
Delete account
A member's ability to close their own account and have their data erased, without asking support to do it for them. This self-service surface is still being built — right now, account settings is the page a member reaches for every other self-service action, and it will be where this one lands too.
Opens in a new tab. Not signed in yet? You'll be sent to sign in first, then straight back here.
Forgot password
Type your email to receive a link that lets you set a new password, without needing the old one.
What this exercises
POST /v1/auth/forgot-password { "email": "[email protected]" }
→ 200 { "message": "..." } (always 200, enumeration-safe)
POST /v1/auth/reset-password { "token": "...", "new_password": "..." }
→ 200 { "message": "Password reset successfully" }
Resend verification email
If the first verification email was missed, deleted, or expired, a member can ask for a new one without contacting support.
What this exercises
POST /v1/auth/resend-verification { "email": "[email protected]" }
→ 200 { "message": "..." } (always 200, enumeration-safe)