AUTH_MODE environment variable and cannot be changed at runtime.
Auth Modes
Clients (custom frontends, the JS/Python SDKs, the official console) discover the active mode via the unauthenticated
GET /info endpoint and branch their login UI accordingly.
Login Flows
otp
- Client posts email to
POST /auth/login - Sinas sends a 6-digit code (valid 10 min by default)
- Client posts code to
POST /auth/verify-otpand receives access + refresh tokens
password
- Client posts email + password to
POST /auth/login - Sinas verifies and immediately returns access + refresh tokens
password+otp
- Client posts email + password to
POST /auth/login - On password match, Sinas sends a 6-digit OTP and returns an OTP session id
- Client posts code to
POST /auth/verify-otpand receives access + refresh tokens
Tokens
All modes issue the same JWT pair:- Access token — short-lived (default 15 min), sent as
Authorization: Bearer <token> - Refresh token — long-lived (default 30 days), exchanged at
POST /auth/refresh
Superadmin Bootstrap
The first admin user is seeded from environment variables on backend startup:SUPERADMIN_EMAIL— email of the user to create / promote to AdminsSUPERADMIN_PASSWORD— only used whenAUTH_MODEispasswordorpassword+otp. Setting this on a running deployment and restarting the backend will reset the password — the escape hatch for “admin lost their password.”
Password Reset
For modes that include passwords:- User-initiated — not implemented when
AUTH_MODE=password(no email channel guaranteed). Use admin reset. - Admin-initiated — an admin generates a one-time reset link from the user management page in the console and delivers it out-of-band (Slack, in person).
- Lost-superadmin escape hatch — set
SUPERADMIN_PASSWORDin the env and restart the backend.
API Keys
For programmatic access (scripts, CI/CD, integrations), create API keys instead of using short-lived JWT tokens. Each key has its own set of permissions (a subset of the creating user’s permissions). API keys work identically across all auth modes.Authorization: Bearer <key> or X-API-Key: <key> headers. Keys can have optional expiration dates.
Token Exchange (bring your own auth)
If your application already authenticates users itself, you don’t need to run a second Sinas login flow. Your backend exchanges its knowledge of “who is logged in” for Sinas tokens:- An admin links your application’s user ids to Sinas users as external identities — or you let the exchange auto-provision them.
- Your backend calls the exchange endpoint with an API key holding the
sinas.auth.exchange:allpermission:
- The response contains a normal Sinas access + refresh token pair for that user, which your frontend uses against the runtime API. All ownership scoping (
:own), permissions, and audit logging apply as if the user had logged in directly.
- The user is resolved by
(provider, subject). If unknown andemailis given, an existing user with that email is matched and the identity is linked to them. - With
auto_provision: true, unknown users are created and assigned theTOKEN_EXCHANGE_DEFAULT_ROLE(defaultGuestUsers).emailis required to provision. metadatais stored on the identity and refreshed on every exchange.custom_fieldsis shallow-merged into the user’s custom fields (partner keys win, admin-set keys survive).provisioned: truein the response indicates the exchange created the user.
sinas.auth.exchange:all like any other admin credential.