Aller au contenu

ADR-0003 — Identité fédérée : ActuaryLab adopte le standard du socle (Levier C)

Statut Accepté — 2026-06-09
Portée backend/ (SSO, RBAC, mapping persona), module ActuaryLab
Décision ActuaryLab se conforme au modèle d'identité fédérée Levier C du socle Quantis ; il n'invente pas son propre modèle
Réf. normative Quantis-app/docs/IDENTITE_FEDEREE.md (§4.2 Levier C, §5 politique persona)

🎯 À retenir

ActuaryLab se conforme au modèle d'identité fédérée du socle (Levier C) et n'invente pas le sien : le jour des personas, il clonera le pattern d'EmpreinteCarbone (table sso_link, provisioning JIT au persona le plus bas, élévation par match d'email). Aujourd'hui l'ADR fixe la direction et trace l'écart — aucune table persona n'est créée (YAGNI).

Contexte

Le socle Quantis a déjà tranché son modèle d'identité fédérée — documenté dans Quantis-app/docs/IDENTITE_FEDEREE.md (« L'identité fédérée dans l'écosystème Quantis »), acté auprès de la Direction BFEV le 2026-05-22. C'est une chaîne SSO à trois niveaux YODICORE (IdP) → Quantis (Consumer + IdP) → Module (SP), et le choix retenu est le Levier C (hybride) :

  • Quantis (IdP) ne transporte qu'un rôle binaire universel dans le SLT (tenant_admin | tenant_user) : il authentifie et nomme le tenant, sans RBAC métier.
  • Le module détient son propre RBAC : table de jointure sso_link (provider, external_sub, persona, local_user_id, status) + écran d'admin. Provisioning JIT conservateur : un inconnu reçoit le persona le plus bas (active) ; l'élévation vers un persona puissant se fait uniquement par match d'email avec un compte pré-créé — jamais via le claim role (anti-escalade cross-tenant, §5.1).

EmpreinteCarbone est l'implémentation de référence de ce modèle (Phase R, en production, table sso_links + resolve_sso_persona, trois cookies de session par persona).

État actuel d'ActuaryLab. Le module est un SSO Consumer pur (backend/app/sso.py) : il vérifie le SLT auprès de /verify/, crée le Tenant à la volée, ouvre une session signée. Il porte le claim role dans la session (session.py) mais ne le mappe à aucun persona : il n'a ni table sso_link ni tables persona (seul Tenant existe). Il est donc conforme-mais-incomplet : conforme parce qu'il ne fait aucun mapping abusif (la règle « ne pas auto-mapper role → persona » est respectée par le fait qu'il ne mappe rien) ; incomplet parce qu'il n'a pas encore de RBAC métier.

Question à trancher : quand ActuaryLab aura besoin de personas distincts (actuaire junior / senior / externe / consultant client — cf. plan d'implémentation), quel modèle de mapping adopte-t-il ?

Options considérées

  • A — Adopter le standard du socle (Levier C, pattern EC) : implémenter une table sso_link et l'élévation par match d'email, en clonant le pattern déjà éprouvé sur EmpreinteCarbone.
  • B — Inventer un modèle propre à ActuaryLab : par exemple traduire mécaniquement le claim role en persona, ou un mapping ad-hoc spécifique au module.
  • C — Rester SSO Consumer pur indéfiniment : ne jamais introduire de personas ; un seul niveau d'accès pour tout utilisateur authentifié du tenant.

Comparatif pour / contre

Critère A — Standard socle (Levier C) B — Modèle propre C — Consumer pur
Conformité IDENTITE_FEDEREE.md ✅ par construction ❌ diverge du standard ✅ (trivialement)
Sécurité (anti-escalade cross-tenant) ✅ match d'email, éprouvé ❌ risque si role→persona ✅ (rien à mapper)
Réutilisation (pattern EC en prod) ✅ clone direct, ~100 tests amont ❌ tout réécrire ✅ rien à écrire
Cohérence inter-modules Quantis ✅ même grammaire que EC ❌ deux modèles divergents ⚠️ neutre
Couvre les personas du plan ✅ junior/senior/externe/client ⚠️ selon impl ❌ impossible
Coût aujourd'hui ⚠️ non nul (mais différé) ❌ élevé + risqué ✅ nul

Décision

Option A — ActuaryLab adopte le standard d'identité fédérée du socle (Levier C). Il n'invente pas son propre modèle (B écarté : divergence + risque d'escalade) et n'est pas condamné au mono-accès (C écarté : le plan prévoit des personas distincts). Quand le besoin de personas se concrétisera, le module clonera le pattern d'EmpreinteCarbone : table sso_link, provisioning JIT conservateur (persona le plus bas par défaut), élévation par match d'email avec compte pré-créé par un Admin du module — jamais par traduction du claim role.

Ce que cette décision tranche aujourd'hui (sans code) : la direction. Aucune table persona n'est créée maintenant (YAGNI : ActuaryLab n'a pas encore de feature différenciée par rôle). La valeur de cet ADR est de fixer le standard cible et de tracer l'écart, pour qu'on n'improvise pas un modèle divergent le jour venu.

Conséquences

  • Écart tracé (dette de conformité, assumée). ActuaryLab porte role dans la session mais ne l'exploite pas ; il n'a ni sso_link ni RBAC métier. C'est volontaire au stade actuel (Jalon 0 + moteur DBO). L'écart à combler = sso_link + élévation par email, le jour des personas.
  • Aucune dépendance directe à EC. On clone le pattern, pas le code (EC reste un dépôt séparé) — comme le déclencheur A→C des ADR-0001/0002, c'est une réutilisation de design, pas un couplage de build.
  • Garde-fou de sécurité pré-acté. Le mapping role → persona puissant est interdit ; seule l'élévation par match d'email est autorisée (cohérent avec la politique socle §5.1).

⚠️ Piège

Le claim role du SLT est tenant-scopé : le traduire mécaniquement en persona puissant ouvre une escalade cross-tenant. L'élévation passe uniquement par match d'email avec un compte pré-créé. Un inconnu reçoit toujours le persona le plus bas.

  • Doc. L'Architecture renvoie à cet ADR pour la trajectoire d'auth ; la note normative reste Quantis-app/docs/IDENTITE_FEDEREE.md.

Déclencheur de réexamen (pré-enregistré)

Dès qu'ActuaryLab introduit une feature différenciée par rôle (≥ 2 personas, p. ex. un écran réservé à l'actuaire senior, ou un parcours consultant client distinct), implémenter le pattern sso_link du socle (cette décision passe alors de direction à code). Tant qu'un seul niveau d'accès suffit, l'état « Consumer pur » reste conforme et il n'y a rien à construire.