Operator sessions and step-up
IBEX does not put a PAT in browser storage for Console use. The operator flow exchanges a PAT for short-lived, server-owned cookies, then uses a separate step-up token for actions that need fresh proof. The session machinery is implemented locally; hosted identity, origin, and Console integration remain gated.
Flow
PAT → POST /v1/operator/session/login
→ HttpOnly access cookie
→ HttpOnly refresh cookie
→ JS-readable CSRF cookie
→ POST /refresh rotates access (and, with Auth, the refresh family)
→ POST /logout revokes the session family and clears cookies
The browser never receives the upstream PAT from these responses, and the API never treats cookie presence alone as authorization.
Routes
All routes are under /v1/operator/session and return 503 SERVICE_DEGRADED when IBEX_OPERATOR_FEATURE_ENABLED is false.
| Method | Path | Input | Behavior |
|---|---|---|---|
POST | /login | JSON { "pat": "ibex_pat_..." } | Validates the PAT, issues access/refresh cookies, sets a CSRF cookie |
POST | /refresh | Refresh cookie | Rotates access; in Auth-owned mode also rotates the refresh token |
POST | /logout | Access and/or refresh cookie | Revokes the session family when Auth-owned, then clears cookies |
GET | /me | Access cookie or bearer fallback | Returns the verified session subject and organization |
Login accepts a PAT in the request body once. Do not persist that PAT in browser storage, URLs, or logs.
Issuance modes
| Environment | Signing and verification | Status |
|---|---|---|
| Development | HS256 via JWT_HMAC_SECRET; access 1 hour by default | Provisional compatibility stub |
| Staging/production | AuthService issues RS256 access/refresh; API verifies with DASHBOARD_JWT_PUBLIC_KEYS_PEM | Implemented local path; hosted Console evidence remains open |
AuthService defaults are 15-minute access, 7-day refresh, and 5-minute step-up tokens. The API's development stub has different defaults because it is a local convenience path, not the production issuer. Do not treat the two TTL sets as interchangeable.
The API explicitly does not accept JWT_PRIVATE_KEY_PEM as HMAC material. Signing keys stay in AuthService; the API only holds public verification keys.
Cookies and CSRF
| Cookie | HttpOnly | Purpose |
|---|---|---|
ibex_session | Yes | Short-lived access token |
ibex_refresh | Yes | Refresh token |
ibex_csrf | No; deliberately readable by the Console | Double-submit CSRF token |
Cookie security is deployment-owned:
DASHBOARD_COOKIE_SECURE=trueis required outside development.DASHBOARD_COOKIE_SAMESITEislax,strict, ornone;nonerequiresSecure.DASHBOARD_COOKIE_DOMAINis set only when the UI and API intentionally share a parent domain.- CORS uses an explicit
IBEX_ALLOWED_ORIGINSallow-list. Credentialed CORS with*is rejected.
Cookie-authenticated mutations require a matching X-CSRF-Token; bearer-PAT callers are exempt. Login is CSRF-exempt because it establishes the session.
TOTP and step-up
AuthService owns the TOTP primitives:
| RPC | Purpose |
|---|---|
BeginTotpEnrollment | Start enrollment and return an otpauth:// URI |
ConfirmTotpEnrollment | Confirm enrollment with a code |
CreateStepUpToken | Verify TOTP and mint a short-lived step-up JWT |
IssueOperatorSession | Mint RS256 access/refresh JWTs for an authenticated caller |
RevokeOperatorSession | Invalidate the session and its refresh family |
TOTP is disabled by default (IBEX_AUTH_TOTP_ENABLED=false). When enabled, AuthService requires JWT_PRIVATE_KEY_PEM, JWT issuer/audience, and Redis so TOTP attempt lockouts are shared across replicas. TOTP secrets are sealed with the credentials master key.
High-impact API actions can require X-IBEX-Step-Up. The proof must bind to the same organization, subject, and session, and AuthService consumes the step-up JTI before the mutation so replay fails. Missing, expired, wrong-org, or replayed proof is denied without leaking whether the target exists. This is an implemented seam, not evidence that every privileged Console action has an accepted UI and audit flow.
Configuration
| Variable | Purpose |
|---|---|
IBEX_OPERATOR_FEATURE_ENABLED | Kill switch for operator session and SSE routes; default false |
JWT_ISSUER / JWT_AUDIENCE | Session claim validation |
JWT_ACCESS_TOKEN_TTL / JWT_REFRESH_TOKEN_TTL / JWT_STEP_UP_TOKEN_TTL | AuthService-issued TTLs |
JWT_PRIVATE_KEY_PEM | AuthService RS256 signing key; never given to the API |
DASHBOARD_JWT_PUBLIC_KEYS_PEM | API verification key set |
JWT_HMAC_SECRET | Development-only provisional HS256 material |
DASHBOARD_CSRF_SECRET | CSRF double-submit key; required outside development |
IBEX_ALLOWED_ORIGINS | Exact Console origin allow-list |
IBEX_AUTH_TOTP_ENABLED | Enables TOTP and step-up RPCs |
Failure behavior
| Condition | Result |
|---|---|
| Operator feature disabled | 503 SERVICE_DEGRADED before route logic |
| Invalid/revoked PAT at login | 401 INVALID_TOKEN |
| AuthService unavailable during login/refresh | 503 SERVICE_DEGRADED; do not fall back to unsigned cookies |
| Missing refresh cookie | 401 INVALID_TOKEN |
| Access and refresh proofs bind different sessions | Logout fails closed and clears cookies |
| Step-up missing, stale, replayed, or wrong subject | Deny; no mutation |
| Public keys missing outside development | 503 SERVICE_DEGRADED |
What remains open
- Console login, enrollment, invites, and mutation UI are not accepted.
- Hosted AuthService/session topology, deployment origin, and credentialed CORS/CSRF evidence are incomplete.
- Two-tenant negative tests, session revocation drills, and rollback runbooks are still required for hosted promotion.
- The development HS256 path is a compatibility stub and must not become the production issuer.
Related
- Auth overview — identity plane and permissions
- Issuing API keys — PAT creation and rotation
- Management API — route families that consume sessions
- Console — presentation and live-read boundary
- Secrets and keys — key ownership and rotation

