Users, sessions and second factors
Password sign-in, emailed codes, authenticator apps and remembered browsers.
This is the flow behind the console's sign-in screen. You need it if you are building something that signs a person in directly. If you are writing a service, use API clients instead: they were made for that, and none of the routes on this page will accept a machine credential.
Signing in
curl -sX POST https://api.eu-par-1.interlaken.ai/api/v1/auth/login \
-H 'Content-Type: application/json' \
-d '{"email":"ada@example.com","password":"…"}'The reply is 200 either way, and the body tells you which of two things happened.
Signed in, when the browser is already trusted:
{
"access_token": "eyJ…",
"refresh_token": "…",
"id_token": "eyJ…",
"token_type": "Bearer",
"expires_in": 3600,
"scope": "…"
}A second factor is wanted, otherwise:
{
"status": "mfa_required",
"challenge": "ch_…",
"method": "email",
"email_hint": "a••@example.com",
"resend_after_seconds": 30,
"mail_sent": true
}Branch on status. A client that assumes the first shape will hand a user an
undefined token and no explanation.
method is totp when the account has an authenticator app enrolled, and email
otherwise. On the email path, mail_sent: false means the code could not be
delivered, from a send cap or a provider failure; show the resend button rather than
an error.
Completing the challenge
curl -sX POST https://api.eu-par-1.interlaken.ai/api/v1/auth/mfa/verify \
-H 'Content-Type: application/json' \
-d '{"challenge":"ch_…","code":"482913","remember_device":true}'code is whichever of three things the user has: the emailed code, the current code
from their authenticator app, or a recovery code, which is recognisable by its
rc-xxxxx-xxxxx shape.
Success returns the same token body as a clean sign-in. Failure is specific, and the distinction matters because each one asks for different words on screen:
| Status | error | What to say |
|---|---|---|
| 401 | invalid_code | Wrong code. The body's attempts_left says how many tries remain. |
| 429 | retry_later | Too many attempts. retry_after_seconds says how long. |
| 410 | challenge_expired | This sign-in attempt timed out. Start again from the password. |
| 503 | secret_encryption_not_configured | Authenticator codes are unavailable in this deployment. |
POST /api/v1/auth/mfa/resend mails a fresh code, and only applies to the email
method. Respect resend_after_seconds.
Remembered browsers
Passing remember_device: true sets a cookie called interlaken_device, good for
30 days, after which that browser skips the second factor. The value is opaque and
the platform stores only its hash.
The cookie's domain is the parent domain every availability zone shares, so a
browser trusted in one zone is trusted in the others without signing in again. It is
Secure, HttpOnly and SameSite=None, which means a browser client must send
credentials on cross-origin requests for any of this to work.
GET /api/v1/auth/security lists the remembered browsers alongside the account's
other security state, and marks which entry is the browser asking. Removing one is
DELETE /api/v1/auth/devices/{id}; removing all of them is
DELETE /api/v1/auth/devices.
Authenticator apps
POST /api/v1/auth/totp/setup → { "secret": "…", "otpauth_url": "otpauth://…" }
POST /api/v1/auth/totp/confirm → { "recovery_codes": ["rc-…", …] }Render otpauth_url as a QR code, take one code back to prove the app works, and
show the recovery codes once. They are not retrievable later.
POST /api/v1/auth/totp/recovery-codes replaces them and
DELETE /api/v1/auth/totp turns the app off; both ask for the password again.
Seeds are sealed at rest. In a deployment with no encryption key configured, enrolment fails closed rather than storing a seed in the clear.
Keeping and ending a session
POST /api/v1/auth/token refresh, with the refresh token
POST /api/v1/auth/logout revoke this access token, and the refresh token if sent
POST /api/v1/auth/sessions/revoke-all end every session this user has anywhereRevocation is checked on every request in every zone, so signing out everywhere is effective immediately rather than eventually.
Account upkeep
change-password, forgot-password and reset-password behave as their names
suggest. send-verify-email and verify-email confirm an address.
PUT /api/v1/auth/locale sets the language the platform writes mail in, and it
follows the account across zones, so a choice made once holds everywhere.