Loi 25 Québec
Art. 5 — accès limité. MI : identité par app, droits DB minimaux ; surface publique sans PII — SECURITY-SQL-ADMIN.md
Document de référence pour comprendre tous les flux d’auth de GS-Apps.
Lecture 15 min. Audience : devs, auditeurs sécurité.
Date : 2026-04-27 (mise à jour 2026-06-22) | Version : 1.1
GS-Apps utilise 2 systèmes d’autorisation en production (+ 1 historique retiré) :
| Système | Pour quoi | Cible |
|---|---|---|
Azure AD claims groups | Routes Admin api-portail | api-portail (Phase 1) |
| (retiré 2026-06-22) | — | |
| ug.UserCompanyBcRoles (Système C) | Multi-company UG — source unique des rôles | En prod (admin, hub, inv, ug, portail) |
Tous se basent sur Azure AD comme source de vérité pour l’identité (qui tu es).
La différence est où réside l’autorisation (ce que tu as le droit de faire).
sequenceDiagram participant S as Signaleur participant B as Browser participant P as api-portail participant Q as SQL ptl.Stickers S->>B: scan sticker QR B->>P: GET /p/QR-XXXXX P->>Q: SELECT sticker (non PII) Q-->>P: métadonnées P-->>B: SSR HTML mobile Note over P,B: Aucune session utilisateur
Exports D2 : docs/architecture/diagrams/rendered/neutral/auth-flow-public-scan.svg et docs/architecture/diagrams/rendered/terminal/auth-flow-public-scan.svg.
Aucune authentification requise. La page mobile /p/QR-XXXXX est publique.
Flux :
https://portail.groupesignalisation.ca/p/QR-00001SELECT sur ptl.Stickers (colonnes non-PII exposées publiquement)Sécurité : aucune donnée personnelle sur la surface publique ; conformité Loi 25 pour cette zone — détail dans PUBLIC_VS_PRIVATE.md sur GitHub.
sequenceDiagram participant U as Utilisateur participant SPA as SPA React participant AD as Azure AD participant API as APIs internes U->>SPA: ouvre Hub/Admin/Inv/Fit SPA->>AD: loginRedirect / silent token AD-->>SPA: JWT (audience API) SPA->>API: Bearer JWT API->>API: valide + ug.UserCompanyBcRoles
Exports D2 : docs/architecture/diagrams/rendered/neutral/auth-flow-msal.svg et docs/architecture/diagrams/rendered/terminal/auth-flow-msal.svg.
Stack : MSAL.js navigateur + Azure AD + audience JWT partagée.
Flux :
https://hub.groupesignalisation.ca/loginRedirect() (pattern standard monorepo — éviter loginPopup() en prod)aud=api://<client_id> envoyé en Bearer aux APIsug.UserCompanyBcRoles pour autoriser/refuserSécurité : cache MSAL sécurisé, refresh silencieux, MFA par policy AAD.
flowchart LR U[Utilisateur] -->|navigateur| P[api-portail SSR] P -->|Authorization Code + PKCE| AD[Azure AD] AD -->|tokens| P P -->|HttpOnly cookies| U classDef interne fill:#dbeafe,stroke:#1e40af,color:#1e40af class P,AD interne
Exports D2 : docs/architecture/diagrams/rendered/neutral/auth-flow-oauth-pkce.svg et docs/architecture/diagrams/rendered/terminal/auth-flow-oauth-pkce.svg.
Stack : OAuth Authorization Code + PKCE côté serveur Node.js (api-portail).
Pourquoi pas MSAL : api-portail est SSR (server-side), pas SPA. PKCE est le pattern moderne pour serveurs publics sans secret client.
Sécurité : PKCE-only, HTTPS forcé, cookies HttpOnly + SameSite=Lax, redirectUri strictement listé dans App Registration.
flowchart TB subgraph Apps["Apps Node"] A1[api-portail] A2[gs-hub / admin / inv / fit] end MI[Managed Identity<br/>System-assigned] SQL[(Azure SQL)] Apps --> MI MI -->|azure-active-directory-default| SQL classDef interne fill:#dbeafe,stroke:#1e40af,color:#1e40af class A1,A2,MI interne
Exports D2 : docs/architecture/diagrams/rendered/neutral/auth-flow-managed-identity.svg et docs/architecture/diagrams/rendered/terminal/auth-flow-managed-identity.svg.
Stack : Managed Identity System-Assigned + tedious 16+ azure-active-directory-default.
Sécurité : aucun mot de passe SQL en App Settings pour GS_Apps ; MI scopée par app ; journalisation côté Azure (voir ADR-033).
Cible : api-portail uniquement.
Constantes dans apps/api-portail/src/middleware/requireAuth.ts :
const AZURE_GROUP_ADMIN = '7a9bf9f7-dde9-44ff-86c1-deee829462c4';const AZURE_GROUP_GEST = 'e1594515-5d26-4b84-b14c-30f69d7d3ae6';const AZURE_GROUP_USER = '8dd154cd-c43e-4fd8-aa4f-50f385d94486';Gestion : Azure Portal → Entra ID → Groups → ajouter/retirer des membres.
Statut : DÉCOMMISSIONNÉ 2026-06-22. Table orpheline, 0 lecteur runtime. Migration DROP 136 écrite HORS SQL_FILES — application prod = David db_owner. Section conservée pour historique ADR-055 / SUB-D.
Cible : admin, hub, inv, ug, portail + Console UG (control plane).
Matrice : user_id × company_id × bc_key × role. Conçu pour multi-company UG (ADR-032).
Statut : source unique d’autorisation en production (fusion B→C complétée admin 2026-06-22).
| Aspect | Système A (groups) | Système B (ug.UserAppRoles) | Système C (multi-company) |
|---|---|---|---|
| Source données | Azure AD | Azure SQL | Azure SQL |
| Multi-company | Non | Non | Oui |
| Granularité | App-level | App-level | App × BC × Compagnie |
| Gestion | Azure Portal UI | (retiré) | Console UG |
| Apps qui utilisent | api-portail | (aucune — retiré) | admin, hub, inv, ug, portail |
| Statut | Prod | Retiré 2026-06-22 | Prod |
La fusion B→C et le retrait de ug.UserAppRoles sont livrés côté repo (mig 136 DROP écrite, application prod = STOP David). Reste : porter shophub en C.
flowchart LR B[ug.UserAppRoles RETIRÉ] --> C[ug.UserCompanyBcRoles PROD] C --> R[Reste: shophub → C] classDef interne fill:#dbeafe,stroke:#1e40af,color:#1e40af class B,C,R interne
Exports D2 : docs/architecture/diagrams/rendered/neutral/multi-company-roadmap.svg et docs/architecture/diagrams/rendered/terminal/multi-company-roadmap.svg.
ug.UserCompanyBcRoles en BDug.UserAppRolesresolveDisplayRole, 2026-06-22)ug.UserAppRolesshophub en C (reste)flowchart TB L1[Identité — Azure AD + MFA] L2[Auth — MSAL / PKCE / MI] L3[AuthZ — groups / DIM / futur C] L4[Audit + réseau + secrets] L1 --> L2 --> L3 --> L4 classDef layer fill:#fef2f2,stroke:#D92231,stroke-width:2px,color:#7f1d1d class L1,L2,L3,L4 layer
Exports D2 : docs/architecture/diagrams/rendered/neutral/auth-security-layers.svg et docs/architecture/diagrams/rendered/terminal/auth-security-layers.svg.
ptl.AccessLog (futur, ADR-032 §5) + Azure SQL Auditing (à activer)Loi 25 Québec
Art. 5 — accès limité. MI : identité par app, droits DB minimaux ; surface publique sans PII — SECURITY-SQL-ADMIN.md
ISO 27001
A.9.4 — accès privilégiés. ADR-033, fin du partage CloudSA34b30667, break-glass documenté.
OWASP A04:2021
Insecure Design — OAuth PKCE, MI, pas de secret SQL en clair pour GS_Apps.