Architecture Decision Record

0016. The login session and front-channel SSO model

Context

Single sign-on means a principal logs in once and then obtains tokens for many relying parties without re-authenticating. That requires a session — a stateful, front-channel anchor at the admission plane that remembers "this browser is this principal, authenticated to this assurance, until this time." A session is emphatically not a token: tokens are short-TTL, self-contained, and verified statelessly at the edge (ADR-0003, ADR-0010); the session is the longer-lived browser-side fact from which tokens are minted. The risk this ADR must avoid is the Keycloak/Authentik pattern the constitution exists to refuse (ADR-0001): a globally strongly-consistent session store consulted on every request, which would be a shared-core dependency on the hot path. Vord must have SSO without that.

Decision

  1. The SSO session is opaque server-side state, keyed by a secure cookie, owned by Modgud (ADR-0009). The cookie carries no authority of its own; it references a session record. The record holds: the principal; the realm (ADR-0012); the authentication context (amr/acr, ADR-0015) and its auth_time (step-up and OIDC max_age decisions need when, not only how); the session lifetime; the relying parties the session has minted tokens for (back-channel logout, decision 5, must know whom to notify); and a re-evaluation flag so adaptive authentication (ADR-0031) can mark a live session "step-up required". Every session is assigned a session id (sid), stamped into each token minted from it (the ADR-0003 claim register) — the linkage that lets logout name the tokens to kill (decision 4) and back-channel logout name the session (decision 5).

  2. The session lives in a SessionStore that is eventually-consistent and edge-/region-local — explicitly NOT the strongly-consistent core. This is the canonical application of constitution rule 2: a session is not an irreducible fact (not issuance, not uniqueness, not freshness-critical revocation), so it is exactly the kind of state the constitution says must be cacheable and edge-local rather than strong. Sessions are regional and may be sticky to the zone that created them; there is no global strong session read on any path.

  3. Session ≠ token, and the hot path sees only tokens. Verification (Vör) never reads the session — it reads the signed token the session helped mint. The session's job is upstream of issuance (decide whether to mint without re-authenticating); once a token exists, it stands on its own. So rule 1 is untouched: the session is part of the issuance side (Modgud), which rule 1 does not govern.

  4. Logout and session revocation reuse the revocation machinery. RP-initiated logout (ADR-0014) and, later, SAML single-logout (ADR-0017) end the session and propagate via the same signed status-list + NATS path as token revocation (ADR-0005) — not a strong global write. Concretely the kill is by sid: tokens carry their minting session's sid, so a single status-list entry covers every token the session minted, with no per-token enumeration (ADR-0044). "Log me out everywhere" is therefore bounded-freshness, not instantaneous, the same honest trade revocation makes, and it never introduces a core read on the verification path.

  5. Logout propagates to relying parties, not only to Vord. Ending a session kills the Vord-issued tokens (decision 4), but a relying party that minted its own application session from those tokens would otherwise stay logged in — the "logged out of the IdP, still logged into the app" gap. Vord therefore supports OIDC Back-Channel Logout (and Front-Channel Logout where a back channel is unavailable): on session termination, Modgud notifies the relying parties holding a session for that principal, so single-logout is complete, not merely Vord-side. (Front-Channel Logout — iframe- and third-party-cookie-dependent, increasingly broken by browsers — is declined; the endpoint set is pinned in ADR-0044.) The notification is an issuance-side (Modgud) concern that never touches the Vör hot path (rule 1); its delivery is best-effort within the same freshness budget as revocation (ADR-0005, ADR-0039). A relying party that misses it still sees the underlying tokens fail within their short TTL, so back-channel logout tightens the window rather than being the sole mechanism.

Consequences

  • Vord delivers SSO — one login, many relying parties — without the global strong-session store the constitution forbids; the session is regional and eventually-consistent, and the hot path never depends on it.
  • The session is the attachment point every front-channel protocol shares: OIDC RP-initiated logout, SAML SSO/SLO (ADR-0017), and step-up re-authentication (ADR-0020) all hang off this one model rather than inventing their own.
  • Single-logout is complete: back-channel logout notifies relying parties so a downstream application session ends with the Vord session, closing the "logged out of the IdP but still logged into the app" gap — with the short token TTL as the backstop when a notification is missed.
  • Session-termination latency ("logged out everywhere within N seconds") is the same freshness budget as revocation (ADR-0005) and is instrumented the same way — a security/freshness-plane signal, not a correctness gap.
  • Because the session carries the authentication context, a token minted from it inherits the right amr/acr (ADR-0015) without re-running the ceremony — which is what makes SSO and step-up (re-auth when the session's assurance is insufficient) both expressible on one model. Inheriting the context does not skip policy: every mint re-runs conditional access (ADR-0031), and a session whose re-evaluation flag is set mints nothing until re-authentication clears it.
  • Browserless (native, mobile, CLI) clients have no cookie; their long-lived anchor is the refresh token, which is session-bound with the same sid linkage and logout semantics — the refresh-token family model is ADR-0044.