auth

Create Session

Verify a sign-in credential and set the HttpOnly session cookie.

The session is bound to the identity that authenticated, so every request it later authenticates resolves a user and that user's active organization rather than only "a credential was presented once". The response names both, so a client knows who it is signed in as without a second call.

The rate-limit check deliberately runs only after a failed verification, not before it: a pre-verification gate can't know whether this attempt would have succeeded, so once an IP has used up its failure quota it would end up blocking that IP's legitimate owner too, not just further attackers. Running after verification also means the throttle bounds how many verdicts an IP gets, not how much work it can cause: a password attempt pays for a bcrypt verification (cost 12, on the order of 200ms of CPU, and one is burned against a stand-in hash even for an address nobody holds) before the limit is consulted, so a 429 costs the same as a 401. A gateway exposed to the internet should rate-limit this path at the proxy as well.

The maintenance-mode check runs before either credential is verified, and refuses both. Before, because a frozen deployment should not spend a bcrypt verification per attempt and the refusal is not about the credential anyway; both, because the way back out is the master key against PATCH /v1/settings/maintenance-mode through the header, which never passes through this door. That is what keeps the way back out off the frozen path, and it is why no identity needs an exemption here; an operator who no longer holds the master key recovers by setting OTARI_MASTER_KEY and restarting, which is a restart rather than a click. It leaks nothing either: GET /v1/bootstrap already publishes the same flag unauthenticated, so the sign-in screen can render the right page.

post/v1/auth/session

Request body

emailstring nullable

The identity's sign-in address.

master_keystring nullable

The gateway master key; verified once and never stored by the browser. Accepted only while the operator identity has no password, which is to say while nobody has claimed this deployment (see GET /v1/bootstrap).

passwordstring nullable

The identity's password.

Example request

{
  "email": "operator@example.com",
  "password": "a-real-password"
}

Response

Successful Response

active_organization_idstring uuid required

The organization that identity is acting in, which scopes every tenancy surface.

expires_atstring date-time required

When the session cookie stops being accepted.

user_idstring uuid required

The identity this session speaks for.

Changes