Commit Graph

206 Commits

Author SHA1 Message Date
Julien Gautier 065d50247f docs: add ADR-0011 for MFA enforcement (Entra Conditional Access + step-up hooks)
Pin the MFA policy: enforcement lives in Entra ID Conditional Access at
the tenant level (org IT responsibility) - the application code does not
implement MFA mechanics. The BFF performs a defense-in-depth sanity-check
on the id_token amr claim at session creation; sessions without evidence
of multi-factor authentication are rejected. The accepted amr values are
maintained in a small in-source list and reviewed on cadence.

Step-up MFA is designed-in for v1 but dormant: a @RequireMfa() decorator
and a RequireMfaGuard ship in the codebase, the session payload carries
mfaVerifiedAt, and the SPA HTTP interceptor handles the 401 + claims
challenge round trip. No v1 route is annotated, since v1 has no admin UI
or other operations sensitive enough to require fresh MFA. The hooks are
kept alive by automated tests so they don't drift.

Authentication Context Classes (ACR-based step-up) are not used in v1;
they remain a future option if specific operations later demand them.
Service-account / app-only flows are out of scope.

decisions/README.md index updated. CLAUDE.md gains an explicit 'MFA' line
pointing to ADR-0011.
2026-04-29 23:16:51 +02:00
Julien Gautier b6e43565c3 docs: add ADR-0010 for session management (opaque ID + Redis + AES-GCM)
Pin the session-state architecture: the browser carries only an opaque
crypto-random session id in __Host-portal_session, signed with
SESSION_SECRET. The payload (userId, audience, curated claims, encrypted
tokens, timestamps) lives in self-hosted Redis, accessed via the standard
express-session + connect-redis pair under the NestJS Express adapter.

The id_token / access_token / refresh_token tuple is encrypted with
AES-256-GCM before being stored - per-record IV, GCM auth tag - using
SESSION_ENCRYPTION_KEY. A Redis snapshot or memory dump alone is not
enough to forge a working session; the encryption key must also be
compromised. Tampered or wrong-key records are rejected and audited.

TTL policy: idle 30 min sliding (TTL refreshed on each request) +
absolute 12 h (checked in a global interceptor, triggers DEL on expiry).

Topology: Redis Sentinel (3+ nodes) in prod with TLS and ACL; single node
in dev. Operational specifics deferred to a phase-3 infrastructure ADR.

Revocation is immediate (DEL session:{id}). A secondary index
user_sessions:{userId} supports per-user listing and force-logout. No
PostgreSQL mirror; historical trace lives in the future audit-log ADR.

decisions/README.md index updated. CLAUDE.md gains an explicit 'Sessions'
line pointing to ADR-0010.
2026-04-29 23:09:34 +02:00
Julien Gautier 90bca95fce docs: add ADR-0009 for the authentication flow (OIDC Auth Code + PKCE via MSAL Node)
Pin the BFF authentication mechanics: OAuth 2.0 Authorization Code Flow
with PKCE, executed server-side via @azure/msal-node's
ConfidentialClientApplication. Tokens are held in the BFF session and
never reach the browser; the SPA only ever sees the opaque
__Host-portal_session cookie.

Token validation enforces the tenant allowlist from ADR-0008 (iss check)
and maps the audience claim to our Audience enum at the validation step.
Refresh-token rotation is enabled via MSAL acquireTokenSilent. Cookies
use the __Host- prefix (forces Secure/Path=/, no Domain) with
HttpOnly/SameSite=Lax. CSRF uses the double-submit pattern with a
matching X-CSRF-Token header on every state-changing request, enforced
by a NestJS interceptor and injected client-side by an Angular HTTP
interceptor. Logout is RP-initiated against Entra's end_session_endpoint.

Routes are pinned: GET /auth/login, GET /auth/callback, POST
/auth/logout, GET /auth/me. AuthGuard is registered globally - public
routes must be explicitly opted in.

Local dev runs over HTTPS via mkcert to keep cookie behaviour identical
to prod. All Entra-specific values come from environment variables; the
BFF refuses to start without them.

decisions/README.md index updated. CLAUDE.md gains an explicit
'Authentication flow' line pointing to ADR-0009.
2026-04-29 22:59:33 +02:00
Julien Gautier f5e8e6ef61 docs: add ADR-0008 for the identity model (multi-tenant Entra workforce + dual-audience design)
Capture the v1 identity model: Microsoft Entra ID, multi-tenant app with
B2B guest invitation for partner-org employees, workforce-only
authentication in v1. Code and data are architected for dual audience
from day one (Audience enum, audience claim in sessions, audience column
+ RLS policies on shared tables, claims-based authz) so that adding Entra
External ID for customers later is a switch-flip rather than a refactor.

The dev environment uses a Microsoft 365 Developer Program tenant (free,
renewable) to unblock work while the prod tenant is being provisioned by
the org IT contact. Production requires Entra ID P1 licensing - flagged
here so it can be planned, not surprised.

decisions/README.md index updated. CLAUDE.md 'Identity' line now points
to ADR-0008.
2026-04-29 22:53:17 +02:00
Julien Gautier 084ff5c3bf docs: add ADR-0007 for pre-commit hooks and align documentation references
- decisions/0007-pre-commit-hooks-and-conventional-commits.md formalizes
  Husky + lint-staged + commitlint with Conventional Commits as the local
  quality-gate baseline.
- decisions/README.md index updated.
- docs/setup/03 section 8 rewritten to reference the ADR and document the
  full hook setup (pre-commit, commit-msg, commitlint config).
- docs/setup/03 future-work table 'ADR(s)' column removed; future ADR
  numbers are now assigned at the moment each ADR is written, not
  pre-reserved.
- CLAUDE.md aligned: pre-allocated phase-2 ADR numbers replaced by phase
  references; a pointer to ADR-0007 added under 'Local quality gates'.
2026-04-29 21:01:49 +02:00
Julien Gautier 79eee77594 chore: initialize repository with project rules, docs, and phase-1 ADRs
Set up the foundation for the adastra-portal project:

- CLAUDE.md captures durable project rules (quality bar, security/perf/a11y
  as first-class, language, commit conventions, ADR proactivity).
- docs/ and decisions/ scaffolding with maintained indexes (docs/README.md
  and decisions/README.md), MADR 4.0.0 template, and tag vocabulary.
- Phase-1 ADRs (0001-0006) lock structural choices: ADR usage, Nx monorepo
  with the apps preset, naming convention (adastra-portal / portal-shell /
  portal-bff), Angular CSR/zoneless/Signals/Vitest, NestJS over Express,
  PostgreSQL with Prisma.
- docs/setup/ guides translated to English.
- .gitignore covers Node/Nx artifacts and the personal notes/ scratchpad.

The Nx workspace itself is not yet bootstrapped; that step is gated on a
revised setup guide aligned with the ADRs.
2026-04-29 20:43:00 +02:00