Commit Graph

2 Commits

Author SHA1 Message Date
Julien Gautier ed6cca499d feat(portal-bff): session middleware with AES-256-GCM at rest per ADR-0010
CI / commits (pull_request) Successful in 2m46s
CI / scan (pull_request) Failing after 2m47s
CI / check (pull_request) Successful in 4m24s
CI / a11y (pull_request) Successful in 1m51s
CI / perf (pull_request) Successful in 3m24s
mount express-session + connect-redis at bootstrap on top of the
shared ioredis client. the full json payload is encrypted with
aes-256-gcm before it reaches redis; envelope is versioned
(v1.<iv>.<tag>.<ciphertext>, base64url) so the algorithm or key
derivation can rotate without a flag-day re-encryption.

scope is intentionally infrastructure-only — middleware mounted,
req.session available downstream, cookie set on first write,
encryption-at-rest active. populating req.session.user from
/auth/callback, /me, /auth/logout, and the absolute-timeout
interceptor land in follow-ups.

notable shape choices captured in ADR-0010 (amended here):
- encryption at rest applies to the whole payload, not just a tokens
  sub-field. the session also carries pii claims (oid, tid,
  preferred_username) — encrypting the envelope removes the need to
  classify fields one by one and costs essentially the same.
- connect-redis v9 was rewritten for node-redis v4 and dropped
  ioredis. rather than swap the whole bff to node-redis, a small
  adapter shapes the six commands connect-redis actually calls
  (get / set with {expiration:{type:'EX',value}} / expire / del /
  mGet / scanIterator) to look like node-redis. the rest of the
  bff stays on a single shared ioredis client.

session id: crypto.randomBytes(32).toString('base64url')
cookie name: __Host-portal_session in production, portal_session in
dev (the __Host- prefix mandates Secure which dev http can't
provide). httpOnly + sameSite=lax + path=/. resave:false,
saveUninitialized:false, rolling:true; cookie maxAge follows
SESSION_IDLE_TIMEOUT_SECONDS (default 1800).

env: SESSION_ENCRYPTION_KEY is mandatory (32 bytes after base64url
decode); rejected at boot via a new assertSessionEncryptionKey()
mirroring the other pre-flight validators. SESSION_IDLE_TIMEOUT_SECONDS
and SESSION_ABSOLUTE_TIMEOUT_SECONDS are optional with adr-aligned
defaults (1800 / 43200).
2026-05-12 17:56:52 +02:00
Julien Gautier 0e58e32d29 chore: relocate ADRs from decisions/ to docs/decisions/ to consolidate documentation
Move the ADR folder under docs/ alongside the rest of the project
documentation. Convention (flat folder, globally-sequential 4-digit
numbering, tags-based categorization, MADR 4.0.0 format) is unchanged
- only the path moved.

- git mv decisions docs/decisions preserves history for all 18 ADRs +
  README + template (19 files renamed in this commit).
- ADR-0001 amended in-place with a dated note documenting the
  relocation. Status remains 'accepted' - the location detail
  changed, the decision did not.
- All cross-references updated:
  - CLAUDE.md (~17 ADR links + 3 mentions of decisions/ in the Project
    rules section)
  - docs/README.md (now references decisions/ as a sibling under docs/)
  - docs/setup/03-angular-nx-monorepo.md (paths shortened from
    ../../decisions/ to ../decisions/, since setup/ and decisions/ are
    now both inside docs/)
  - docs/decisions/0003 ../CLAUDE.md adjusted to ../../CLAUDE.md
    (one extra level of nesting)
  - docs/decisions/template.md mention of the README path
  - notes/asvs-level-decision-briefing-rssi.md mention of the index

Sanity verified: every ADR link in CLAUDE.md, docs/setup/03, and
docs/decisions/0001 resolves to an existing file. pnpm nx run-many
-t lint passes on 8 projects.
2026-04-30 18:57:59 +02:00