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.
1. La chaîne d’isolation
Section intitulée « 1. La chaîne d’isolation »| 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.
2. Choix du modèle
Section intitulée « 2. Choix du modèle »| 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).
3. Identifiants de connexion
Section intitulée « 3. Identifiants de connexion »3.1 Le problème
Section intitulée « 3.1 Le problème »Dans Keycloak, un identifiant est unique dans tout le royaume. Deux clients ne peuvent pas avoir chacun un utilisateur agent01.
3.2 La règle
Section intitulée « 3.2 La règle »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
extest réservé et ne peut pas servir de code d’organisation.
3.3 Saisie par l’utilisateur
Section intitulée « 3.3 Saisie par l’utilisateur »- 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.
3.4 Pourquoi pas le numéro de téléphone
Section intitulée « 3.4 Pourquoi pas le numéro de téléphone »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.
3.5 Conséquences sur le schéma
Section intitulée « 3.5 Conséquences sur le schéma »app.utilisateur.identifiantcontient l’identifiant local, avec l’unicité(organisation_id, identifiant).app.organisation.codedevient immuable, puisqu’il figure dans les identifiants (déclencheur à ajouter).- Un compte externe a une ligne
app.utilisateurpar organisation où il intervient, avec le mêmekeycloak_sub. La contrainte existanteUNIQUE (organisation_id, keycloak_sub)le permet déjà. Ses permissions sont donc propres à chaque organisation.
4. Personnes travaillant pour plusieurs clients
Section intitulée « 4. Personnes travaillant pour plusieurs clients »Cas visés : inspecteur indépendant, consultant, auditeur, équipe support de BioTrace.
4.1 Règles pour l’API
Section intitulée « 4.1 Règles pour l’API »- Si le jeton porte une seule organisation, elle est l’organisation active.
- 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 ligneapp.utilisateuractive existe pour ce compte dans cette organisation. Sinon : refus. - Sans organisation choisie, une requête d’un compte multi-organisations est refusée. L’API ne choisit jamais « par défaut ».
- L’organisation active ne change pas au cours d’une transaction. Aucune requête ne lit deux organisations à la fois.
4.2 Back-office
Section intitulée « 4.2 Back-office »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.
4.3 Application terrain
Section intitulée « 4.3 Application terrain »- 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.
4.4 Invitation
Section intitulée « 4.4 Invitation »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.
5. Réglages communs à tous les clients
Section intitulée « 5. Réglages communs à tous les clients »| 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) |
5.1 Fournisseur d’identité du client
Section intitulée « 5.1 Fournisseur d’identité du client »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.
5.2 Instance dédiée
Section intitulée « 5.2 Instance dédiée »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.
6. Compte de service de l’API
Section intitulée « 6. Compte de service de l’API »6.1 Le risque
Section intitulée « 6.1 Le risque »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.
6.2 Règles
Section intitulée « 6.2 Règles »- 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-realmsur 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 moduleidentitel’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-realmpermet 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). - Un seul module appelle Keycloak. Le module
identitede 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. - 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. - 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.
- 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é.
- Journalisation. Chaque appel d’administration est inscrit au journal d’audit, avec l’organisation, l’auteur et le compte visé.
6.3 Console d’administration Keycloak
Section intitulée « 6.3 Console d’administration Keycloak »- 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.
8. Arrivée et départ d’un client
Section intitulée « 8. Arrivée et départ d’un client »8.1 Arrivée
Section intitulée « 8.1 Arrivée »Une seule opération de l’API, réservée à l’équipe BioTrace, exécutée en une transaction côté base :
- création de
app.organisation(code, pays, devise, fuseau) ; - création de l’organisation Keycloak avec l’attribut
biotrace_organisation_id; - création des ensembles de permissions par défaut ;
- création du premier compte administrateur client, avec second facteur exigé ;
- 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.
8.2 Départ
Section intitulée « 8.2 Départ »- désactivation de l’organisation : plus aucune connexion de ses membres ;
- révocation de toutes les sessions et de tous les appareils ;
- détachement des comptes externes, sans suppression du compte lui-même ;
- export des données au client et durée de conservation : décision à prendre (contrat et RGPD), hors Lot 0.
9. Tests complémentaires
Section intitulée « 9. Tests complémentaires »| 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 |
10. Mises à jour induites
Section intitulée « 10. Mises à jour induites »| 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 |