Julien Gautier 5c346820ea
CI / commits (pull_request) Successful in 1m25s
CI / scan (pull_request) Successful in 1m30s
CI / check (pull_request) Failing after 1m34s
CI / a11y (pull_request) Successful in 1m6s
CI / perf (pull_request) Successful in 3m30s
feat(portal-bff): absolute-timeout middleware + user_sessions index per ADR-0010
closes the ttl-policy + revocation pieces left from #110.

absolute-timeout middleware:
  every request that survives express-session runs through a new
  middleware that checks req.session.absoluteExpiresAt. past the
  12 h hard ceiling, it destroys the redis-side session, clears the
  portal_session cookie, drops the entry from the per-user index,
  and lets the request keep flowing anonymously. route-level
  guards (/me today, future @requireauth) turn that into a 401
  where auth is actually needed — public routes stay accessible.
  not the "every route returns 401" reading of adr-0010 §"ttl
  policy" (validated with the project lead): "returns 401" is
  interpreted as "the user eventually sees a 401 when they touch
  something that needs auth", not as a hard refusal on the
  current in-flight request.

user_sessions:{userId} secondary index (adr-0010 §"revocation"):
  a new UserSessionIndexService maintains a redis set of active
  session ids per user. wired on /auth/callback (sadd at sign-in,
  after session.save() so the session row exists before the index
  points at it) and on /auth/logout + the absolute-timeout
  middleware (srem on destroy). best-effort: a failed sadd/srem
  logs a pino warning and the auth flow continues — the index is
  a convenience for admin operations, not a security invariant.
  orphans (entries whose session:<id> redis key has expired on
  idle ttl) are tolerated per the adr; future consumer code
  filters them out.

  no in-product consumer ships in this pr — the admin "logout
  everywhere" endpoint lands with the admin module (+ @requireadmin
  / @requiremfa guards). today the index is write-only on the bff
  side.

session payload extension:
  createdAt and absoluteExpiresAt are now set on the session at
  the same moment as req.session.user, in /auth/callback. the
  session.types.ts declaration merging exposes them as optional
  SessionData fields so every consumer sees a typed payload.

wiring:
  - SessionModule provides UserSessionIndexService and a
    SESSION_ABSOLUTE_TIMEOUT_MIDDLEWARE token whose factory builds
    the middleware via createAbsoluteTimeoutMiddleware(index,
    logger). same pattern as the existing SESSION_MIDDLEWARE token.
  - AuthModule now imports SessionModule so AuthController can
    inject UserSessionIndexService.
  - main.ts mounts the absolute-timeout middleware immediately
    after the session middleware.

per-user identifier is the entra `oid` claim (stable per-user
inside the tenant, matches req.session.user.oid). future
multi-tenant scenarios may want `${tid}:${oid}` — easy refactor
when external id activation lands (adr-0008). not in scope here.

out of scope, landing in follow-ups:
- admin "logout everywhere" endpoint consuming
  UserSessionIndexService.list(userId)
- audit-pipeline first-class events for session.absolute_timeout
  + user_session_index.* (adr-0013)
- token blob persistence (id_token / access_token / refresh_token)
  in the encrypted session — adr-0014 dependency

123/123 tests pass (was 110); +13 specs across the two new files
and the auth controller spec.
2026-05-12 23:22:08 +02:00

Documentation index

This is the entry point to all project documentation. It is maintained automatically: any addition, rename, or removal of a .md file under docs/ must be reflected here in the same change.

Conventions

  • Documentation is written in English.
  • One topic per file. Group related files into a folder when there are three or more.
  • Cross-reference with relative links so they keep working in GitHub, IDEs, and exported sites.
  • For architectural decisions, do not add them here — they belong in decisions/ as MADR 4.0.0 ADRs.

Sections

Daily development

  • development.md — repo layout, prerequisites, initial setup, daily commands, observability dev-loop (Jaeger UI, log ↔ trace correlation), dependency updates (Renovate), conventional commit cycle. Day-to-day reference for working on the project.

Architecture

  • architecture.md — cross-cutting Mermaid diagrams: C4 system context, C4 containers, Nx module boundaries, CI/CD pipeline. Single-decision diagrams (auth sequence, ERD, etc.) live inline in their ADR.

Onboarding & environment

Setup guides for new contributors:

Operations & runbooks

Empty — to be populated when we deploy.

Security, performance, accessibility

Empty — placeholders to be filled with rationale docs alongside their corresponding ADRs.

S
Description
No description provided
Readme 42 MiB
Languages
TypeScript 85.3%
JavaScript 5.4%
SCSS 4.3%
HTML 3.9%
Shell 0.8%
Other 0.3%