ADR-0028: Stateless Encrypted Token-State Cookie (no server-side session store)¶
- Status: accepted
- Date: 2026-06-30
- Deciders: Nicolas Burri
- Refines: ADR-0011 (OIDC / BFF sessions); relates to ADR-0009, ADR-0012
Context¶
ADR-0011 chose OIDC with server-side (BFF) token handling and an opaque browser cookie. It left open where the tokens live: in a server-side session store (stateful — the cookie is a reference into per-instance or shared state) or in an encrypted token-state cookie (stateless — the cookie itself carries the encrypted tokens). REQ-AUTH-004 was phrased as a "server-side-revocable session reference"; REQ-AUTH-006 requires revocation to be "effective immediately".
Forces: the deployment should scale to redundant service instances without sticky sessions or a replicated session store; and a regulated QMS must be able to revoke access promptly (ISO 13485; Part 11 / Annex 11 if in scope).
Decision¶
Hold the OIDC tokens in an encrypted, httpOnly, Secure, SameSite cookie (Quarkus
web-app token-state); keep no server-side session store.
- Stateless by design. The session is self-contained in the encrypted cookie, so any service instance can serve any request — no session affinity, no shared/replicated session store. This is the primary reason for the choice (horizontal scalability / redundancy).
- Opaque to the client. The cookie is encrypted with a server-held key and is not readable or modifiable by the browser/JS, and carries no client-extractable identity or authorization claims — satisfying the confidentiality intent of REQ-AUTH-004.
- Revocation without server session state (REQ-AUTH-006). Authorization is re-evaluated on every request against live LQMS state — the user's lifecycle status and role assignments are resolved per request (ADR-0009/0012). Therefore:
- administrative revocation is effectively immediate: deactivating a user or removing a role takes effect on that user's next request — no session to kill;
- a DEACTIVATED user is rejected on every authenticated request;
- bounded lifetimes (idle + absolute, REQ-AUTH-005) and short access-token lifetimes limit how long any cookie remains valid;
- logout clears the cookie and performs IdP single / back-channel logout where supported;
- rotating the cookie encryption key invalidates all outstanding cookies at once (a global revocation lever).
Options Considered¶
- Server-side session store (rejected): enables instant per-session kill, but binds sessions to instance/shared state — sticky sessions or a replicated store — which is the scaling cost we want to avoid; the regulator-relevant control (prompt revocation of authorization) is already achieved statelessly by per-request re-evaluation.
Consequences¶
- No session store and no session affinity; deployment can run redundant instances freely.
- Trade-off (accepted): a stateless cookie cannot be force-invalidated server-side in
mid-lifetime; the residual window for a compromised cookie is bounded by token/session
lifetimes rather than closed instantly. Accepted in exchange for scalability — and
mitigated by
httpOnly+Secure+TLS (limiting theft), short access-token lifetimes, and key rotation. Revocation of authorization (the regulated control) is immediate via re-evaluation. - Requires per-request enforcement of user lifecycle status (reject DEACTIVATED) — an authentication augmentor / the request filter — and a documented cookie-encryption-key management / rotation policy.
- Access-token lifetime becomes a security-relevant config knob (revocation latency for the compromised-cookie case).
Requirements¶
Revises REQ-AUTH-004 and REQ-AUTH-006 (pending requirements baseline v1.4).