ADR-038 — OIDC user matching by sub claim, not email¶
Status: PROPOSED — implementation pending Date: 2026-04-27 Sprint: 54 (debt captured), implementation candidate Sprint 55+
Context¶
Aujourd'hui (2026-04-27) on a hit un bug live qui s'est répété sur 2 apps :
- OpenMetadata (catalog.akko-ai.com) : boucle infinie /signin ↔ /auth/callback
- Superset (bi.akko-ai.com) : aurait eu le même bug, prévenu via SQL UPDATE
Cause racine : les users en DB de chaque app ont un email seed (alice@159.195.77.208.nip.io)
qui ne matche plus le JWT publié par Keycloak (alice@akko-ai.com). L'app rejette le login
avec un mismatch d'email malgré un username identique.
Notre fix de jour : SQL UPDATE des emails + helm hook qui re-aligne à chaque upgrade. C'est un bricolage. Si l'email change pour n'importe quelle raison (user édite son email côté Keycloak, fédération SCIM, migration domaine) → ça recasse.
Decision¶
Adopter sub (Keycloak UUID) comme clé de matching primaire, conformément à OpenID
Connect Core 1.0 §2 :
The
subClaim, combined withiss, is the only Claim that an RP can rely upon as a stable identifier for the End-User. Thesubvalue MUST be locally unique and never reassigned.
L'email devient un attribut métier (peut changer), pas la clé primaire.
Consequences¶
Per-app implementation cost¶
| App | Where | Cost | Risk |
|---|---|---|---|
| OpenMetadata 1.12 | jwtPrincipalClaims config |
1 ligne YAML + migration des users existants pour stocker leur sub Keycloak |
RISQUE de doubles users si bascule à chaud sans migration. À tester en sandbox avant prod. |
| Superset 4.1 (FAB) | Custom SecurityManager Python override |
~50 lignes Python qui surchargent auth_user_oauth pour matcher par sub au lieu de username/email |
Code custom à maintenir. Pas de config knob FAB natif. |
| Airflow 3.x (FAB) | Idem Superset | ~50 lignes Python | Idem |
Migration path¶
Pour chaque app :
- Ajouter une colonne
external_id(ou champ JSON) qui stocke lesubKeycloak. - Backfill : pour chaque user existant, lookup son
subvia Keycloak admin API (par username), update la row. - Changer le matching code pour utiliser
external_iden priorité, fallback sur username/email pour les users pas encore backfillés. - Tests E2E : login alice avec email change côté IdP → doit toujours marcher.
Pattern enterprise alternatif (SCIM 2.0)¶
À grande échelle (Dremio, DataHub, Snowflake), l'IdP push les changes user via SCIM. AKKO
a déjà un scim-bot user en DB OpenMetadata mais SCIM n'est pas wiré.
Coût SCIM : plusieurs jours par app (extension Keycloak SCIM + endpoint SCIM côté chaque app).
Sources¶
- OpenID Connect Core 1.0 — sub claim
- OpenMetadata 1.12 Keycloak SSO docs
- Superset OAuth/OIDC discussion
- Airflow FAB auth manager
- Dremio SCIM + OIDC
Current debt (workaround in place 2026-04-27)¶
- PR #118 : Helm init job qui SQL UPDATE les emails OM
user_entity.json->>emailpour les aligner sur${AKKO_DOMAIN}à chaque helm upgrade. - SQL UPDATE manuel sur Superset
ab_user(alice + admin → @akko-ai.com). PAS de hook Helm équivalent — symétrie volontairement non-poussée pour ne pas amplifier le bricolage.
Cette dette est tracée comme ADR-038 implementation dans le backlog Sprint 55+.
Decision pending founder¶
- Investir 3-5 jours sur l'implémentation
sub-based matching (3 apps) ? - OU rester sur le hook re-align tant que le domaine ne change plus (acceptable risque) ?
- OU passer directement à SCIM 2.0 (~10 jours, vraie solution enterprise) ?