Aller au contenu

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)

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.

Une App Registration partagée pour toute la flotteTrois familles de clients — les SPAs MSAL (hub, admin, inv, ug), api-portail en OIDC PKCE, et MCP en JWT — pointent toutes vers la même inscription Entra « GS Apps » : un seul identifiant client, une audience commune. Chaque app n’y apporte que son redirect URI, fusionné de façon additive. L’application d’entreprise associée est en attribution requise.hub · admin · inv · ugSPAs — MSALapi-portailOIDC Auth Code + PKCEMCPJWT OAuth 2.1un redirect URI par app(fusion additive)App Registration « GS Apps »une seule · audience communeEnterprise app : attribution requise
Une inscription, trois familles de clients. Chaque app n’apporte que son redirect URI ; l’« attribution requise » bloque tout compte non assigné avant le code.

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.

Les quatre SPAs partagent le même wrapper (@gs/auth), donc le même comportement :

  • loginRedirect exclusivement — 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.

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)
rôles applicatifs Absents par design — les rôles vivent dans ug.UserCompanyBcRoles et sont résolus en BD à chaque requête