Julien Gautier 3c0b5cab58
CI / commits (pull_request) Successful in 3m22s
CI / check (pull_request) Failing after 3m51s
CI / scan (pull_request) Failing after 3m59s
CI / a11y (pull_request) Successful in 4m23s
CI / perf (pull_request) Successful in 9m14s
feat(auth): @RequirePrivilege/@RequireRole/@RequireScope guards (ADR-0025)
Phase 2 of the ADR-0025 phasing per its §"More Information": the
three new route decorators + guards land alongside the legacy
@RequireAdmin migration. Each guard reads the session-resident
Principal built by the previous PR's PrincipalBuilder, evaluates
the route's requirement, and either passes or emits 403 + audit.

Shared lib (libs/shared/auth):
- principal-matchers.ts: pure principalHasAnyPrivilege /
  principalHasAnyRole / principalCoversResource. The matchers
  live in the shared lib so the SPA can render UI predicates
  with the same logic the BFF enforces server-side. 24 unit
  tests covering OR-composition, empty-requirement degenerate
  case, every scope kind, and the resource-side parentage chain.
- ScopableResource type carrying optional FINESS / delegation /
  region / siege parentage that callers populate from the
  route's protected resource.

BFF guards + decorators (apps/portal-bff/src/auth):
- @RequirePrivilege('Portal.X', ...) + RequirePrivilegeGuard.
- @RequireRole('rh', ...) + RequireRoleGuard.
- @RequireScope(req => extract(req)) + RequireScopeGuard. The
  extractor can be async (Prisma lookup pattern); returning null
  is treated as a denial with empty required[].
- Multiple values within a single decorator are OR-combined;
  stacking decorators is AND-combined at the Nest layer.
- All three guards 401 on anonymous, 403 + audit on
  authenticated denial, pass on match. The 403 body is the
  ADR-0021 structured envelope with a generic 'forbidden' code
  so an attacker cannot enumerate which privilege/role/resource
  a route gates.
- principal-extractor.ts factors the session->Principal read
  with a legacy-session bridge for principals minted before the
  previous PR landed (filters user.roles for Portal.* values
  and synthesises an unrestricted scope).

Audit module:
- New AuthorizationDeniedInput type + audit.authorizationDenied()
  method emitting auth.authorization_denied with a kind
  discriminator (privilege / role / scope) plus required[] and
  held[] arrays. Distinct from the existing admin.access_denied
  event so admin-surface signals stay clean.

Legacy guard migration:
- AdminRoleGuard now reads principal.privileges instead of
  user.roles. The public @RequireAdmin() API and the
  admin.access_denied event type are unchanged; only the audit
  row's rolesHeld field now carries Portal.* values rather than
  the raw legacy roles claim.
- MeController.capabilities() likewise reads
  principal.privileges for canAccessAdmin.

Tests:
- 41 shared-auth tests (24 new for matchers).
- 244 new BFF tests covering the 3 new guards, the
  principal-extractor (incl. legacy bridge), the audit method,
  and the 19-persona matrix.
- Total: 741 portal-bff tests, 41 shared-auth tests; full lint +
  build green on nx affected.

Test plan:
- pnpm nx affected -t lint test build --base=main: 3 projects green
- pnpm nx format:check: clean
2026-05-23 21:47:01 +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%