Auth
Client-side auth — anonymous, Apple, Google, email signup / login, account linking, refresh, logout.
The client-side auth surface. Every route accepts an X-Api-Key header (the project's client key). All token verification is project-scoped — a token issued for project A is rejected with 401 INVALID_TOKEN when presented for project B, even if the signature is valid.
Source: apps/api/src/routes/client/auth.ts.
Endpoints
| Method | Path | Description |
|---|---|---|
| POST | /client/auth/anonymous | Create an anonymous user and return session + refresh tokens. |
| POST | /client/auth/social | Apple / Google sign-in via identity token exchange. |
| POST | /client/auth/email/signup | Email + password signup (bcrypt-hashed). |
| POST | /client/auth/email/login | Email + password login. |
| POST | /client/auth/link | Link an anonymous / logged-in account to an Apple / Google identity. |
| POST | /client/auth/refresh | Rotate session + refresh tokens. |
| POST | /client/auth/logout | Revoke a refresh token (idempotent). |
Auth token shape
All success responses return:
session_token is a short-lived JWT; refresh_token is a long-lived JWT backed by a app_user_sessions row (sha256 hashed). On /refresh we verify the stored hash matches and that the session row hasn't been revoked.
POST /client/auth/anonymous
Create an anonymous user. Returns tokens immediately — no credentials to collect.
Request
No body required.
Response 201
Standard auth response plus anonymous_id:
Errors
500 CREATE_FAILED.
Try it:
/client/auth/anonymouscurl -X POST 'https://api.amba.dev/client/auth/anonymous'Curl:
POST /client/auth/social
Apple / Google sign-in. The identity token is verified against the provider's JWKS with strict audience validation (aud must match the project's bundle_id for Apple, google_oauth_client_id for Google). Missing audience config is fail-closed.
Request
| Field | Type | Required | Description |
|---|---|---|---|
provider | "apple" | "google" | yes | |
token | string | yes | Provider identity token. |
session_token | string | no | Existing session for automatic upgrade. |
Response 200
Standard auth response.
Errors
400 AUDIENCE_NOT_CONFIGURED— project has no configured audience for the provider.401 INVALID_TOKEN— identity token signature or audience verification failed.404 NOT_FOUND— project not found.500 CREATE_FAILED/SOCIAL_LOGIN_FAILED.
Try it:
/client/auth/socialcurl -X POST 'https://api.amba.dev/client/auth/social'Curl:
POST /client/auth/email/signup
Request
| Field | Type | Required |
|---|---|---|
email | string | yes |
password | string | yes |
display_name | string | no |
Response 201
Standard auth response.
Errors
400 INVALID_INPUT— missing email / password.409 EMAIL_EXISTS— email already registered.500 CREATE_FAILED.
Try it:
/client/auth/email/signupcurl -X POST 'https://api.amba.dev/client/auth/email/signup'Curl:
POST /client/auth/email/login
Request
| Field | Type | Required |
|---|---|---|
email | string | yes |
password | string | yes |
Response 200
Standard auth response.
Errors
400 INVALID_INPUT.401 INVALID_CREDENTIALS— wrong email / password.500 LOGIN_FAILED.
Try it:
/client/auth/email/logincurl -X POST 'https://api.amba.dev/client/auth/email/login'Curl:
POST /client/auth/link
Link an Apple or Google identity to an existing user (typically an anonymous one, to preserve history). The session token must match the API key's project.
Email linking via this endpoint is explicitly refused — email identities must go through a verified signup / login flow.
Request
| Field | Type | Required |
|---|---|---|
provider | "apple" | "google" | yes |
token | string | yes |
session_token | string | yes |
Response 200
Standard auth response.
Errors
400 UNSUPPORTED_PROVIDER—provider = "email".400 AUDIENCE_NOT_CONFIGURED.401 INVALID_SESSION— session token invalid.401 INVALID_TOKEN— identity token invalid.403 FORBIDDEN— session belongs to a different project.404 NOT_FOUND— project or user not found.409 IDENTITY_ALREADY_LINKED— the social identity is already bound to another account.500 LINK_FAILED.
Try it:
/client/auth/linkcurl -X POST 'https://api.amba.dev/client/auth/link'Curl:
POST /client/auth/refresh
Rotate tokens. The old session row is revoked. Replaying a revoked token returns 401 INVALID_TOKEN — this is how token theft is detected.
Request
| Field | Type | Required |
|---|---|---|
refresh_token | string | yes |
Response 200
Errors
400 INVALID_INPUT.401 INVALID_TOKEN— signature invalid, session not found, project mismatch, expired, or revoked.500 REFRESH_FAILED.
Try it:
/client/auth/refreshcurl -X POST 'https://api.amba.dev/client/auth/refresh'Curl:
POST /client/auth/logout
Revoke the session. Idempotent — invalid tokens return success.
Request
| Field | Type | Required |
|---|---|---|
refresh_token | string | yes |
Response 200
Errors
400 INVALID_INPUT.500 LOGOUT_FAILED.
Try it:
/client/auth/logoutcurl -X POST 'https://api.amba.dev/client/auth/logout'Curl: