5.3 Tokens: JWTs, cookies, and bearer headers
A successful login yields a JWT — an HS512-signed token carrying the principal's id and roles. You present it on later requests one of two ways, and the runtime verifies it on every request.
Two ways to present a token
- Cookie — login sets
rs-auth=<token>(HttpOnly). Browsers send it back automatically; it's the natural lane for a same-site web app. - Bearer header —
Authorization: Bearer <token>. The lane for API clients, cross-origin browser apps (5.5), and server-to-server calls.
The token is the same either way; only the transport differs. The body of the
login response always includes token, so a client that can't or shouldn't use
cookies just reads it from there.
Verification is universal
When auth.jwtSecret is configured, the runtime verifies a presented token on
every request to the tenant, using a constant-time signature check. Two
consequences:
- A valid token's roles are available to every mount's access check (5.4), including internal pipeline hops (which propagate the principal).
- A bad token fails closed. An invalid or expired token returns
401even on an open mount that would accept an anonymous caller. The runtime won't silently ignore a token you sent — if it's bad, the request is rejected.
The practical rule: on 401 with an expired token, drop the token and retry
anonymously (or refresh first), rather than resending the stale one.
Sessions and refresh
Tokens expire after sessionMinutes (default 60). To keep a session alive:
POST /auth/refreshwith a valid token. Past 50% of the token's lifetime it mints a fresh one; before that it returns the current token unchanged (so refreshing early is harmless and cheap).POST /auth/logoutclears the cookie and ends the session client-side.
Lockout
Repeated failed logins (maxAttempts, default 5) lock the account for
lockMinutes (default 10). During lockout, login returns 401 with a
Retry-After header. Because login failures are uniform 401s regardless of
cause, an attacker can't distinguish "wrong password" from "no such user" from
"locked."
A note on statelessness
In the current version, lockout and session state are node-local (not yet shared across a cluster). For a single node this is transparent; for a multi-node deployment, be aware lockout counters aren't shared between nodes yet (tracked deviation — see the runtime status notes).
Next: turning roles into actual access rules on your mounts.
← Previous: 5.2 Users and roles · Manual home · Next: 5.4 Role specs on mounts →