Aller au contenu

Keycloak — fonctionnement multi-clients

Version 1, 29 septembre 2026. Complète la page « Configuration Keycloak » sans la remplacer. En cas de contradiction, cette page prévaut sur la section 3 de la page principale (« Un utilisateur appartient à une seule organisation au pilote »).

  • BioTrace est multi-clients dès le Lot 0. Plusieurs coopératives et entreprises partagent la même instance.
  • Le royaume unique, avec une organisation Keycloak par client, est confirmé.
  • Cinq limites de ce modèle sont traitées ici :
    • l’unicité des identifiants dans tout le royaume ;
    • les personnes qui travaillent pour plusieurs clients ;
    • les réglages communs à tous les clients ;
    • les pouvoirs étendus du compte de service de l’API ;
    • la portée d’une erreur de configuration.
  • La séparation entre clients ne repose jamais sur Keycloak seul. La RLS de la base reste la dernière barrière.
Maillon Où Ce qui garantit l’isolation
1. Identité Keycloak L’utilisateur est membre d’une organisation Keycloak. Le jeton porte cette appartenance (revendication organization)
2. Organisation active API L’API traduit l’organisation Keycloak en organisation_id, et vérifie que l’utilisateur existe dans app.utilisateur pour cette organisation
3. Données PostgreSQL L’API pose app.organisation_id dans chaque transaction. La RLS masque tout le reste
4. Téléphone PowerSync Le jeton PowerSync, émis par l’API, porte un seul organisation_id. Les règles de synchronisation filtrent dessus

Un défaut sur un maillon ne suffit pas à exposer les données d’un autre client. Un jeton Keycloak erroné, par exemple, est arrêté par l’absence de ligne app.utilisateur pour l’organisation demandée, puis par la RLS.

Critère Royaume unique + organisations (retenu) Un royaume par client
Isolation des réglages Réglages communs, sauf fournisseur d’identité par organisation Complète
Montée en charge Adaptée à des milliers d’organisations Se dégrade au-delà de quelques centaines de royaumes
Configuration Une seule, versionnée Dupliquée par client, à maintenir en cohérence
Création d’un client Création d’une organisation, automatisable Création d’un royaume complet
Personnes multi-clients Un compte, plusieurs appartenances Un compte par royaume
Portée d’une erreur Tous les clients Un seul client

Décision. Royaume unique. Un client qui exige une isolation complète relève d’une instance dédiée (section 5).

Dans Keycloak, un identifiant est unique dans tout le royaume. Deux clients ne peuvent pas avoir chacun un utilisateur agent01.

Il existe deux catégories de comptes.

Catégorie Format de l’identifiant Keycloak Exemples Créé par
Compte d’une organisation {code organisation}.{identifiant local} coopa.agent01, coopa.rsci L’administrateur client, via le back-office et l’API
Compte externe (plusieurs organisations possibles) ext.{identifiant} ext.kossi.mensah L’équipe BioTrace, ou par invitation d’une organisation

Règles de format :

  • code organisation : celui de app.organisation.code, en minuscules (Keycloak stocke les identifiants en minuscules) ;
  • identifiant local : ^[a-z0-9_-]{3,32}$ ;
  • le préfixe ext est réservé et ne peut pas servir de code d’organisation.
  • Application terrain. L’agent saisit seulement son identifiant local. Le code de l’organisation est mémorisé à la première configuration de l’application et ajouté automatiquement.
  • Back-office. L’utilisateur saisit son identifiant complet (coopa.rsci).
  • Évolution possible. Une adresse propre à chaque client (coopa.biotrace…) pourra préremplir le préfixe. Hors périmètre du pilote.

Une même personne peut travailler pour deux coopératives, et un numéro peut changer de titulaire. Le téléphone reste un attribut du compte, jamais l’identifiant.

  • app.utilisateur.identifiant contient l’identifiant local, avec l’unicité (organisation_id, identifiant).
  • app.organisation.code devient immuable, puisqu’il figure dans les identifiants (déclencheur à ajouter).
  • Un compte externe a une ligne app.utilisateur par organisation où il intervient, avec le même keycloak_sub. La contrainte existante UNIQUE (organisation_id, keycloak_sub) le permet déjà. Ses permissions sont donc propres à chaque organisation.

Cas visés : inspecteur indépendant, consultant, auditeur, équipe support de BioTrace.

  1. Si le jeton porte une seule organisation, elle est l’organisation active.
  2. Si le jeton en porte plusieurs, chaque requête doit préciser l’organisation choisie (en-tête X-Organisation). L’API vérifie qu’elle figure dans le jeton et qu’une ligne app.utilisateur active existe pour ce compte dans cette organisation. Sinon : refus.
  3. Sans organisation choisie, une requête d’un compte multi-organisations est refusée. L’API ne choisit jamais « par défaut ».
  4. L’organisation active ne change pas au cours d’une transaction. Aucune requête ne lit deux organisations à la fois.

Après la connexion, un compte multi-organisations choisit son organisation dans un sélecteur. Le nom de l’organisation active reste visible en permanence dans l’en-tête. Changer d’organisation recharge l’application.

  • Une installation de l’application est enrôlée pour une seule organisation à la fois (app.appareil.organisation_id).
  • Le jeton PowerSync ne porte qu’une organisation.
  • Pour intervenir pour une autre organisation, l’appareil doit d’abord envoyer sa file de commandes. Il est ensuite désenrôlé, ses données locales sont effacées, puis il est enrôlé à nouveau.
  • Plusieurs organisations simultanées sur un même téléphone : hors périmètre v1.

Une organisation peut inviter un compte externe existant. Son administrateur saisit l’identifiant ext.…. Le rattachement ne devient effectif qu’après acceptation par la personne invitée, à sa prochaine connexion.

Réglage Portée dans le royaume unique Réponse pour un client aux exigences propres
Politique de mot de passe Tout le royaume Fournisseur d’identité du client (section 5.1)
Durées de session Tout le royaume Fournisseur d’identité du client, pour l’authentification initiale ; les durées de session BioTrace s’appliquent ensuite
Second facteur Politique commune ; exigence par groupe Exigence renforcée possible pour tous les membres d’une organisation (groupe dédié)
Page de connexion Thème commun Logo du client affiché après connexion, dans l’application
Langue Par utilisateur –
Fournisseur d’identité externe Par organisation Oui (section 5.1)

Un client qui a son propre annuaire (OpenID Connect ou SAML) le rattache à son organisation Keycloak. Ses utilisateurs se connectent avec leur compte d’entreprise, et sa politique de mot de passe s’applique chez lui.

Les comptes d’agents de terrain restent locaux à Keycloak. Les agents n’ont généralement pas de compte d’entreprise, et l’enrôlement hors-ligne ne doit pas dépendre d’un annuaire externe.

Si un client exige une isolation complète (royaume, base et stockage séparés), il reçoit un déploiement distinct du même code, en option commerciale. Le code ne suppose jamais qu’il n’existe qu’une instance : aucune adresse ni aucun identifiant de client n’est écrit en dur.

Les administrateurs clients n’accèdent jamais à la console Keycloak. Toute gestion des comptes passe par le back-office, donc par l’API, avec son compte de service. Or ce compte agit sur tout le royaume. Keycloak ne limite pas ses droits à une organisation. La séparation entre clients repose donc entièrement sur l’API à cet endroit.

  1. Droits minimaux. Le compte de service n’a que les rôles d’administration nécessaires : gestion et consultation des utilisateurs et des groupes (vérifié par les tests KC). Il n’a aucun droit sur les clients, les fournisseurs d’identité ni la configuration du royaume. La gestion des organisations et de leurs membres exige manage-realm sur Keycloak 26.x, qui permet aussi de modifier le royaume : décision EC-042 (option 2), deux comptes de service. Le second, biotrace-api-organisations, porte seul ce droit, avec son propre secret (BIOTRACE_API_ORGANISATIONS_CLIENT_SECRET, en coffre). Une seule fonction du module identite l’utilise (création d’organisation et rattachement des comptes). Les tests KC vérifient que le premier compte ne gère pas les organisations et que le second les gère sans droit sur les clients ni les fournisseurs d’identité. manage-realm permet de modifier le royaume : le garde-fou est le code (une seule fonction appelante), le secret distinct et l’alerte de supervision sur les événements d’administration hors pipeline (§7).
  2. Un seul module appelle Keycloak. Le module identite de l’API est le seul autorisé à utiliser l’API d’administration de Keycloak. Une règle de lint interdit tout autre import du client d’administration.
  3. Organisation tirée du contexte. Chaque fonction du module prend l’organisation dans la transaction en cours (app.organisation_id), jamais dans le corps de la requête.
  4. Vérification avant chaque action. Avant toute modification, désactivation, réinitialisation ou révocation, le module vérifie que le compte visé est membre de l’organisation active. Pour un compte externe, l’action porte seulement sur son rattachement à l’organisation active, jamais sur le compte lui-même.
  5. Création atomique. Un compte d’organisation est créé avec son appartenance à l’organisation dans la même opération. Un compte sans organisation est supprimé par une tâche de nettoyage et signalé.
  6. Journalisation. Chaque appel d’administration est inscrit au journal d’audit, avec l’organisation, l’auteur et le compte visé.
  • Réservée à l’équipe BioTrace, avec des comptes nominatifs du royaume master, sous second facteur obligatoire.
  • Non exposée sur Internet : accès par le réseau interne du cluster uniquement (politique réseau Kubernetes).

7. Limiter la portée d’une erreur de configuration

Section intitulée « 7. Limiter la portée d’une erreur de configuration »
  • Configuration versionnée dans infra/keycloak/, modifiée uniquement par demande de fusion relue.
  • keycloak-config-cli exécuté d’abord en recette, avec comparaison de l’état attendu et de l’état réel, puis en production.
  • Sauvegarde de la base Keycloak avant chaque application en production.
  • Tests KC exécutés en recette après chaque changement.
  • Alerte de supervision sur les événements d’administration faits hors du pipeline.

Une seule opération de l’API, réservée à l’équipe BioTrace, exécutée en une transaction côté base :

  1. création de app.organisation (code, pays, devise, fuseau) ;
  2. création de l’organisation Keycloak avec l’attribut biotrace_organisation_id ;
  3. création des ensembles de permissions par défaut ;
  4. création du premier compte administrateur client, avec second facteur exigé ;
  5. rattachement des packs filières retenus.

Si l’étape Keycloak échoue, la transaction est annulée. Si c’est une étape en base qui échoue après l’étape Keycloak, l’organisation Keycloak est supprimée.

  1. désactivation de l’organisation : plus aucune connexion de ses membres ;
  2. révocation de toutes les sessions et de tous les appareils ;
  3. détachement des comptes externes, sans suppression du compte lui-même ;
  4. export des données au client et durée de conservation : décision à prendre (contrat et RGPD), hors Lot 0.
id Scénario Résultat attendu
KC-10 Un administrateur de A crée, modifie ou désactive un utilisateur de B Refus
KC-11 Un administrateur de A révoque un appareil de B Refus
KC-12 Un administrateur de A tente de désactiver un compte externe rattaché à A et B Seul le rattachement à A est retiré
KC-13 Création de coopa.agent01 et coopb.agent01 Deux comptes distincts
KC-14 Compte externe rattaché à A et B, requête sans X-Organisation Refus
KC-15 Compte externe rattaché à A seulement, requête avec X-Organisation: B Refus
KC-16 Jeton PowerSync demandé par un compte externe Jeton limité à l’organisation de l’appareil enrôlé
KC-17 Modification du code d’une organisation Refus par la base
KC-18 Import du client d’administration Keycloak hors du module identite Échec du lint
KC-19 Création d’un client : échec simulé à l’étape Keycloak, puis à l’étape base Aucune organisation orpheline, ni en base ni dans Keycloak
Document Modification
Schéma de la base app.utilisateur : unicité (organisation_id, identifiant) et format ^[a-z0-9_-]{3,32}$ ; déclencheur rendant app.organisation.code immuable ; code ext interdit
Protocole de synchronisation Section 12 : le jeton PowerSync ne porte que l’organisation de l’appareil enrôlé ; désenrôlement avant changement d’organisation
Configuration Keycloak Section 3 : remplacée par la section 4 de cette page ; section 4.1 : règle de choix de l’organisation active
API En-tête X-Organisation pour les comptes multi-organisations ; module identite seul client de l’administration Keycloak