Azure AD / Entra ID — inscription, jetons, claims
Vous êtes ici : Référence — l’identité des personnes (Entra ID). Ce qu’Entra ID fait — et ne fait pas — dans GS-Apps : une inscription partagée, des jetons prouvés, zéro rôle dans le jeton. Lecture 8 min. Audience : devs, administrateurs Entra, auditeurs. Date : 2026-07-07 | Version : 1.0 — remplace le stub (matière :
@gs/auth,@gs/auth-multi-tenant, outillage de provision, ADR-029/055/063)
| Question | Réponse |
|---|---|
| Combien d’App Registrations ? | Une, partagée par les SPAs, leurs backends, api-portail et MCP — seuls les redirect URIs diffèrent par app |
| Comment les SPAs se connectent ? | MSAL, loginRedirect exclusivement (jamais de popup), reconnexion silencieuse au chargement |
| Comment les backends vérifient ? | Signature JWKS, algorithme épinglé, audience double (clientId et api://clientId), issuer exact-match v1/v2, puis allowlist tid en BD |
| Le jeton porte-t-il des rôles ? | Non — les rôles sont résolus en BD à chaque requête (Système C) |
| Qui peut se connecter ? | Seuls les comptes assignés à l’application d’entreprise (« Attribution requise » activée — ADR-029) |
1. Une App Registration partagée
Section intitulée « 1. Une App Registration partagée »Toute la flotte s’authentifie contre la même inscription Entra « GS Apps » : les quatre SPAs (hub, admin, inv, ug), leurs backends, le flow OIDC d’api-portail et le serveur MCP.
Conséquences pratiques :
- Un seul identifiant client (audience commune) — un jeton acquis pour le Hub est vérifiable par les autres backends de la flotte.
- Les redirect URIs sont la partie par-app : chaque app ajoute son URI de retour (son domaine custom). L’outillage de provision les fusionne de façon additive (lecture-modification-écriture) — écraser la liste casserait le login de toutes les apps.
- L’application d’entreprise associée est en « Attribution requise » (ADR-029) : un compte non assigné est bloqué par Microsoft avant même d’atteindre le code — c’est la « porte 2 » du parcours de connexion.
2. Côté SPA — la mécanique MSAL
Section intitulée « 2. Côté SPA — la mécanique MSAL »Les quatre SPAs partagent le même wrapper (@gs/auth), donc le même comportement :
loginRedirectexclusivement — jamais de popup (bloqueurs, mobile). À l’ouverture, une reconnexion silencieuse est tentée une seule fois (SSO de session Microsoft) avant d’afficher quoi que ce soit ; une détection de boucle d’authentification protège contre les redirections infinies.- La configuration vient de variables d’environnement injectées au build (
VITE_AZURE_AD_*) — mêmes valeurs pour toutes les apps, seul le redirect URI diffère. - Le jeton envoyé au backend est choisi avec soin : jamais un jeton d’audience Graph, jamais un jeton expirant dans moins de 60 secondes. La chaîne de récupération est progressive : cache → rafraîchissement forcé → reconnexion silencieuse (
prompt: 'none') — l’utilisateur ne voit un écran de login que si tout a échoué. - Sur un 401, le client HTTP (
@gs/api-client) retente une fois avec un jeton rafraîchi de force — jamais de boucle de retry sur 401/403.
3. Côté backend — la validation
Section intitulée « 3. Côté backend — la validation »Le validateur partagé (@gs/auth-multi-tenant) applique la même discipline partout :
| Contrôle | Règle |
|---|---|
| Signature | Clés JWKS Microsoft (endpoint /common — multi-tenant), algorithme épinglé (RS256 uniquement) |
| Audience | Double forme acceptée : clientId ou api://clientId — rien d’autre |
| Issuer | Exact-match (jamais de sous-chaîne) contre les deux formats Microsoft (v1 et v2), reconstruits avec le tid du jeton |
| Tenant | tid vérifié contre l’allowlist en BD (ug.Companies.azure_tid, cache court) — le contrôle critique du multi-tenant, fail-closed |
| Utilisateur | Résolu par (tid, oid) dans ug.Users — inconnu → refus (ou provisioning JIT là où c’est explicitement activé) |
Deux variantes assumées : le hub embarque un validateur local allégé (identité seule, zéro requête SQL — c’est un portail de liens) ; api-portail valide en mono-tenant avec son flow OIDC PKCE et un cookie de session (voir API — api-portail).
4. Ce que contient un jeton — et ce qu’il ne contient pas
Section intitulée « 4. Ce que contient un jeton — et ce qu’il ne contient pas »| Claim | Utilisation |
|---|---|
tid |
Tenant d’origine — comparé à l’allowlist, sélectionne la compagnie |
oid |
Identifiant stable de la personne — résolution dans ug.Users |
email / preferred_username, name |
Affichage et rattachement |
groups |
Fallback d’affichage côté SPA seulement (avant la réponse du backend) — jamais une source d’autorisation (ADR-063) |
Absents par design — les rôles vivent dans ug.UserCompanyBcRoles et sont résolus en BD à chaque requête |
Pour aller plus loin
Section intitulée « Pour aller plus loin »- Modèle d’accès — les trois portes, la délégation, le god-view.
- Stratégie d’authentification — les quatre cas d’usage (public, MSAL, PKCE, Managed Identity).
- Managed Identity — l’identité des applications elles-mêmes (service-to-service).
- Quick start — la première connexion, côté utilisateur.