diff --git a/docs/decisions/0009-auth-flow-oidc-pkce-msal-node.md b/docs/decisions/0009-auth-flow-oidc-pkce-msal-node.md index d0b2fb6..147fbe1 100644 --- a/docs/decisions/0009-auth-flow-oidc-pkce-msal-node.md +++ b/docs/decisions/0009-auth-flow-oidc-pkce-msal-node.md @@ -60,6 +60,37 @@ The SPA must never hold tokens — that is the BFF security pattern, recommended ## Decision Outcome +The end-to-end flow at a glance: + +```mermaid +sequenceDiagram + autonumber + actor U as User (browser) + participant SPA as portal-shell + participant BFF as portal-bff + participant E as Microsoft Entra ID + participant R as Redis (session store) + + U->>SPA: clicks "Sign in" + SPA->>BFF: GET /auth/login?returnTo=… + BFF->>BFF: generate state + nonce
+ code_verifier + code_challenge + BFF-->>U: 302 → Entra /authorize
(client_id, code_challenge, state, nonce, scope) + U->>E: authenticate
(Conditional Access enforces MFA) + E-->>U: 302 → BFF /auth/callback?code&state + U->>BFF: GET /auth/callback?code&state + + BFF->>BFF: verify state matches
(CSRF defence on the OIDC roundtrip) + BFF->>E: POST /token
(code + code_verifier + client_secret/cert) + E-->>BFF: id_token + access_token + refresh_token + + BFF->>BFF: validate id_token
(sig, iss in allowlist, aud, exp/nbf)
+ sanity-check amr (ADR-0011)
+ map audience claim → Audience enum + BFF->>R: SET session:{opaque_id}
payload incl. tokens encrypted AES-256-GCM (ADR-0010) + BFF-->>U: 302 → returnTo
+ Set-Cookie __Host-portal_session
+ Set-Cookie __Host-portal_csrf + U->>SPA: render destination (cookies attached on subsequent calls) +``` + +The numbered steps line up with the prose below: library / flow / tokens / token validation / refresh / cookies / CSRF / routes / auth layer. + **Library.** `@azure/msal-node`, instance of `ConfidentialClientApplication`, configured with the multi-tenant authority and the tenant allowlist from ADR-0008. PKCE is used despite the confidential-client setup, per IETF current BCP. **Flow.** OAuth 2.0 Authorization Code Flow with PKCE, executed entirely on the BFF. The SPA never sees `code`, `code_verifier`, or any token.