Aller au contenu principal

Ressources

Keycloak Organizations : un realm partagé ou un realm par client pour un SaaS B2B ?

Keycloak Organizations ou realm par client pour un SaaS B2B : droits de l'administrateur d'un client, contenu du jeton et tests sur Keycloak 26.8.2.

L'équipe CTN Solutions · publié le · 16 min de lecture

Faits vérifiés sur les sources primaires le

Avant le premier client, un éditeur SaaS B2B doit décider où vivent les comptes de ses clients dans Keycloak. Un realm par client isole tout, mais chaque nouveau client répète la configuration et ajoute un émetteur à valider côté API. Un realm partagé avec Organizations supprime cette répétition, mais il faut alors démontrer que l'administrateur du client A ne voit ni ne modifie les membres du client B, et que le jeton d'un utilisateur n'ouvre que les données de ses organisations.

La conséquence d'une délégation mal bornée est directe : l'administrateur d'un client peut lire l'annuaire de tous les autres. Cet article donne une décision, les critères qui la fondent et les résultats d'essais sur Keycloak 26.8.2 pour chaque cas. Il s'adresse au CTO ou au lead developer qui doit trancher, pas à qui découvre Keycloak.

Ce que le SaaS B2B demande à son IAM

L'IAM, la gestion des identités et des accès, doit répondre à trois questions avant tout choix de modèle. Première question : quelle isolation vos clients exigent-ils ? Certains demanderont une séparation nette des comptes et des réglages, d'autres se satisferont d'une séparation logique. Deuxième question : combien de clients, et à quel rythme ? Un onboarding hebdomadaire ne supporte pas une étape manuelle à chaque arrivée. Troisième question : qui administre quoi ? Un client veut inviter ses collaborateurs et gérer son fournisseur d'identité (IdP, le service qui authentifie ses utilisateurs), sans toucher aux autres clients.

Keycloak répond avec deux notions à ne pas confondre. Un realm est un espace isolé de comptes, de clients et de réglages. Une organisation regroupe des membres à l'intérieur d'un realm : elle sépare logiquement, mais elle n'isole pas à elle seule. Organizations est entièrement supporté depuis Keycloak 26 et s'active realm par realm. Un membre géré (managed) ne peut exister hors de son organisation ; un membre non géré (unmanaged) garde son identité dans le realm, même sans organisation.

Exemple : deux clients, entreprise-alpha et entreprise-beta, partagent un realm nommé saas-demo. Un collaborateur commun, claire, appartient aux deux organisations. Un second realm joue le rôle du fournisseur d'identité d'un client, lié à l'organisation correspondante.

Le choix entre Keycloak et un IdP managé se traite dans Keycloak, c'est quoi et quand le choisir (ou pas) face aux IdP managés et aux alternatives open source. L'extension Phase Two, un projet distinct, n'est pas couverte ici : l'article ne traite que la fonctionnalité native.

Un realm par client ou des organisations dans un realm : la matrice de décision

Les deux modèles reçoivent la même configuration applicative. Sur Keycloak 26.8.2, la configuration de deux clients demande 6 appels d'administration dans un realm partagé et 14 avec un realm par client. Pour ajouter un client, le realm partagé demande trois étapes (créer l'organisation, créer le fournisseur, le lier), contre huit pour un realm dédié (créer le realm, six étapes de configuration, créer le fournisseur).

Configurer deux clients : appels et étapes

Le graphique compare, pour chaque modèle, le nombre d'appels de configuration pour deux clients et le nombre d'étapes pour ajouter un client.

Schéma défilable horizontalement ; sa version textuelle complète suit.

Lire le schéma sous forme textuelle
  1. Appels de configuration pour deux clients, realm partagé : 6.
  2. Appels de configuration pour deux clients, un realm par client : 14.
  3. Étapes pour ajouter un client, realm partagé : 3.
  4. Étapes pour ajouter un client, un realm par client : 8.
Comptage d'opérations de configuration.

Le tableau reprend les critères qui décident du modèle.

Critère ; Un realm par client ; Organisations dans un realm
CritèreUn realm par clientOrganisations dans un realm
Appels de configuration pour deux clients146
Étapes pour ajouter un client83
Émetteurs (iss) que l'API doit accepterUn par realm, soit deuxUn seul
Collaborateur présent chez deux clientsDeux comptes distincts, un par realmUn compte, membre de deux organisations
Administration déléguéeUn administrateur de realm couvre tout le realmDélégation par organisation, voir le chapitre 4
Départ d'un clientSuppression du realmSuppression de l'organisation, testée (chapitre 5)
Fournisseur d'identité partagéUn par realmUn fournisseur lié à plusieurs organisations depuis Keycloak 26.8.0

Une idée répandue, reprise dans des guides plus anciens, veut qu'un fournisseur d'identité ne soit lié qu'à une seule organisation. Depuis Keycloak 26.8.0, c'est faux : un même fournisseur peut être lié à plusieurs organisations, avec une configuration indépendante pour chaque lien. Pour un même fournisseur, une seule organisation peut porter le type MANAGED, et MANAGED exige l'ajout automatique (guide 26.8.2). Le tableau suit la version courante.

Le modèle par realm n'est pas à écarter pour autant. Il est justifié quand un client exige un annuaire et des réglages séparés. Dans les autres cas, la configuration répétée par client se gère mieux en code, comme le montre l'article Piloter la configuration Keycloak en GitOps : adoption, dérive et suppression de champs.

Ce que contient le jeton, et ce qu'il ne garantit pas

Une application demande le contexte organisationnel par un scope. Le scope organization est optionnel pour tous les clients par défaut. L'application le demande sous trois formes : organization pour les organisations de l'utilisateur, organization:* pour toutes celles dont il est membre, ou organization:<alias> pour une seule. Pour les tests, les jetons s'obtiennent par le flux direct grant ; une application utilise le flux authorization code avec PKCE (une preuve de clé pour l'échange de code).

# Raccourci de test (direct grant). Application : authorization code avec PKCE.
curl -s -d client_id=app-saas -d grant_type=password -d username=claire \
  --data-urlencode "scope=openid organization:*" \
  https://sso.example/realms/saas-demo/protocol/openid-connect/token

Le claim organization (une information contenue dans le jeton) donne le résultat suivant pour alice, membre de l'organisation alpha, et claire, membre des deux.

Scope demandé ; Claim pour alice ; Claim pour claire
Scope demandéClaim pour aliceClaim pour claire
openidabsentabsent
openid organization["entreprise-alpha"]absent
openid organization:*["entreprise-alpha"]["entreprise-beta", "entreprise-alpha"]
openid organization:entreprise-alpha["entreprise-alpha"]["entreprise-alpha"]
openid organization:entreprise-betaabsent["entreprise-beta"]

Trois constats découlent du tableau. Le scope organization seul ne renvoie aucun claim pour claire, qui appartient pourtant à deux organisations : l'application doit donc demander organization:* ou un alias explicite. Demander l'alias d'une organisation dont on n'est pas membre ne produit aucune erreur : le jeton est émis, et le claim est absent. Le contrôle côté API doit donc traiter l'absence comme un refus. Selon le guide 26.8.2, avec le mapper Organization Group Membership (ou les rôles de groupe), le claim devient un objet indexé par alias : une API qui teste « l'alias figure dans une liste » doit alors lire ce format. Le format observé sur 26.8.2 est une liste d'alias.

Extrait du jeton obtenu avec organization:* par claire.

{
  "iss": "https://sso.example/realms/saas-demo",
  "azp": "app-saas",
  "sub": "user-claire",
  "scope": "openid profile organization:* email",
  "organization": ["entreprise-beta", "entreprise-alpha"]
}

Le routage par domaine ajoute une étape avant le jeton. Keycloak rattache un domaine d'e-mail à une organisation et peut changer le flux de connexion : si le fournisseur lié a la redirection automatique, l'utilisateur part vers son IdP sans voir le formulaire. La note de version 26.8.2 corrige par ailleurs une prise de contrôle de compte dans ce login identity-first d'une organisation, reproduite sur la version 26.8.1. Un domaine d'e-mail n'est donc pas une autorisation : une adresse non vérifiée côté fournisseur peut passer la porte de domaine, selon la même note. La fédération de l'IdP est détaillée dans OIDC ou SAML : choisir le protocole et fédérer Keycloak avec Entra ID.

Un appel à l'API, du jeton au code 200 ou 403

Un appel est accepté si le jeton est valide et si l'alias demandé figure dans son claim organization.

Schéma défilable horizontalement ; sa version textuelle complète suit.

Lire le schéma sous forme textuelle
  1. L'utilisateur demande un jeton avec le scope organization:*. Keycloak renvoie un jeton signé, dont le claim organization contient les alias de ses organisations.
  2. L'utilisateur appelle l'API sur l'organisation entreprise-alpha, avec le jeton en Bearer.
  3. L'API vérifie la signature, l'émetteur et l'expiration du jeton.
  4. Elle cherche l'alias demandé dans le claim organization.
  5. Alias présent : réponse 200 avec les documents de l'organisation.
  6. Alias absent : réponse 403. Sans jeton valide, la réponse est 401.
Modèle explicatif du contrôle appliqué par l'API.

Le jeton indique donc pour qui agit l'appelant. Il ne dit pas quelles lignes de base de données ou quels dossiers l'application doit renvoyer : ce sont ses requêtes et son stockage qui isolent les données. Le contrôle de l'API lit le bon claim ; il ne démontre pas à lui seul l'isolation des données d'un SaaS.

Déléguer l'administration d'un client sans ouvrir tout le realm

Keycloak sépare deux niveaux de droits d'administration. Le premier est le rôle du realm, depuis la version 26.7.0 : manage-organizations donne la lecture et l'écriture de toutes les organisations et de leurs membres, sous réserve des droits sur les utilisateurs, view-organizations la lecture seule, query-organizations la recherche sans accès complet. Le second est la permission fine par organisation : depuis la même version, les organisations deviennent une ressource des permissions fines d'administration (FGAP, Fine-Grained Admin Permissions), avec les scopes view et manage. Le piège est connu : le rôle manage-realm garde implicitement la gestion complète des organisations, pour compatibilité. Un administrateur qui le possède n'a donc aucun périmètre.

Admin-alpha reçoit les scopes view et manage sur la seule organisation alpha, par une politique qui cible son compte. Quatre configurations sont testées : C2 et C3 partent de C1 et ajoutent un droit, C4 part de C1 avec un droit sur un seul compte. Le tableau compare le résultat attendu pour un administrateur limité à alpha et le résultat obtenu. Les cellules en écart portent la mention « écart ».

Action d'admin-alpha ; Attendu ; C1 : manage sur alpha seule ; C2 : C1 et voir tous les utilisateurs ; C3 : C1 et gérer tous les utilisateurs ; C4 : C1 et gérer le seul compte dave
Action d'admin-alphaAttenduC1 : manage sur alpha seuleC2 : C1 et voir tous les utilisateursC3 : C1 et gérer tous les utilisateursC4 : C1 et gérer le seul compte dave
Lister les membres d'alpha200403, écart200200403, écart
Lister les membres de beta403403403403403
Lister tous les utilisateurs (GET /users)403403200, écart (7 comptes)200, écart (7 comptes)403
Ajouter dave à alpha201403, écart403, écart201201
Ajouter bruno, membre de beta, à alpha403403403201, écart403
Modifier le profil de bruno, membre de beta403403403204, écart403
Modifier l'organisation beta403403403403403
Supprimer beta403403403403403

Les écarts se concentrent sur la liste des membres, l'ajout de membres et l'accès à tous les utilisateurs. L'invitation par courriel dépend d'un serveur SMTP configuré ; cette ligne n'est pas comparable. Les autres cas, comme ajouter un membre à beta, donnent 403 dans les quatre configurations.

Trois constats découlent de la matrice. D'abord, la permission manage sur une organisation ne suffit pas à gérer ses membres : en C1, la liste et l'ajout sont refusés. Ensuite, les droits sur les utilisateurs du realm sont globaux. En C2, voir tous les utilisateurs rend la liste d'alpha lisible, mais GET /users renvoie sept comptes, ceux de beta compris. En C3, gérer tous les utilisateurs permet à l'administrateur d'alpha de modifier un membre de beta et de l'ajouter à alpha. Enfin, la permission sur un seul compte (C4) évite la fuite, mais elle ne se gère pas à l'échelle : chaque membre demande une permission, et la liste des membres reste refusée.

Chaîne de délégation testée pour un administrateur de client

Chaque boîte décrit ce que la configuration laisse passer ou refuse. C2 et C3 ajoutent un droit à C1 ; C4 le restreint à un seul compte.

Schéma défilable horizontalement ; sa version textuelle complète suit.

Lire le schéma sous forme textuelle
  1. C1, permission manage sur alpha seule : lecture et modification d'alpha possibles (200 et 204) ; liste et ajout de membres refusés (403).
  2. C2, C1 et voir tous les utilisateurs : liste des membres d'alpha acceptée (200) ; GET /users renvoie sept comptes, dont ceux de beta.
  3. C3, C1 et gérer tous les utilisateurs : modification d'un membre de beta (204) et son ajout à alpha (201).
  4. C4, C1 avec un droit sur le seul compte dave : aucune fuite ; liste des membres d'alpha refusée (403).
  5. Aucune des quatre configurations ne donne le périmètre attendu.
C4 repart de C1 avec un droit sur un seul compte.

Le rôle global se comporte autrement. Dans la même instance, manage-organizations lit et modifie les deux organisations (200 et 204), mais n'ajoute aucun membre (403) : l'ajout demande aussi des droits sur les utilisateurs. Avec une API qui n'autorise que sur le claim organization, ce rôle ne donne accès à aucun document (403 pour alpha comme pour beta) : administrer les organisations et lire les données de l'application restent deux droits distincts, et c'est à votre API de les garder distincts.

Un second point concerne le client qui sert aux appels d'administration. Sur Keycloak 26.8.2, un jeton émis pour le client applicatif app-saas a reçu 401 sur tous les appels de l'API d'administration, y compris pour le compte à droits globaux, alors que le client admin-cli a obtenu les réponses attendues. Par précaution, gardez un client d'administration distinct du client de l'application : l'application obtient ses jetons pour son API, et l'administration obtient les siens pour Keycloak.

Les groupes d'organisation (26.6.0) ne sont pas traités ici. Pour les réglages de sécurité d'un realm, l'article Sécurité Keycloak : vingt points de configuration à vérifier et à corriger donne une liste de contrôle complémentaire.

Vérifier l'accès croisé dans l'API et gérer le départ d'un client

Une petite API lit le claim organization du jeton validé : elle répond 200 si l'alias demandé s'y trouve, 403 sinon, et 401 sans jeton valide. Tous les cas testés donnent le résultat attendu ; le tableau reprend les principaux.

Appelant et scope ; Documents d'alpha ; Documents de beta
Appelant et scopeDocuments d'alphaDocuments de beta
alice, organization:*200403
bruno, organization:*403200
claire, organization:*200200
admin-alpha, organization:*200403
claire, organization:entreprise-alpha200403

Avec le scope organization seul ou openid seul, claire reçoit 403 sur alpha.

Le départ d'un client se traite comme la suppression de son organisation. Sur Keycloak 26.8.2, la suppression laisse le realm dans l'état suivant.

Ce que la suppression d'une organisation laisse dans le realm

Le type de membre, fixé par le lien avec le fournisseur d'identité, décide du sort du compte à la suppression.

Schéma défilable horizontalement ; sa version textuelle complète suit.

Lire le schéma sous forme textuelle
  1. Membre géré (lien MANAGED) : son compte est supprimé avec l'organisation.
  2. Membre non géré ajouté par l'API : son compte est conservé.
  3. Membres non gérés de l'organisation alpha : les trois comptes restent après la suppression d'alpha par son administrateur.
  4. Membre créé par connexion avec un lien par défaut : son compte est conservé, car le type de membre est UNMANAGED.
  5. Fournisseur d'identité lié : il reste dans le realm et répond après la suppression.
Résultats sur Keycloak 26.8.2. Un lien créé avec les réglages par défaut donne des membres non gérés.

Le point qui surprend est le lien par défaut. Depuis Keycloak 26.8.0, les nouveaux liens entre un fournisseur et une organisation prennent le type UNMANAGED après une mise à niveau. Dans notre essai sur 26.8.2, un membre créé par connexion avec les réglages par défaut est donc resté après la suppression. Le type se règle sur le lien entre l'organisation et le fournisseur, par un appel à l'API d'administration :

PUT /admin/realms/saas-demo/organizations/{id-organisation}/identity-providers/idp-zeta
Content-Type: application/json

{"autoMembership": true, "membershipType": "MANAGED"}

Ce même appel renvoie 204. Dans notre essai sur 26.8.2, un PUT sur le fournisseur lui-même, avec ses organizationLinks, renvoie aussi 204 mais ne change rien : le réglage passe par le lien. Les nouveaux clients créés après une mise à niveau doivent donc vérifier ce réglage dès le départ. La montée de version vers 26.8 est traitée dans Monter de version Keycloak sans casser la production : politique de support, migration de base et retour arrière.

Enfin, le même administrateur a pu supprimer alpha elle-même (204) : le droit manage sur une organisation couvre sa suppression. Ce droit doit donc être distribué avec la même prudence qu'un droit d'administration. Pour un SaaS qui met en production son realm sur Kubernetes, l'offre Mise en production Kubernetes : ouvrir par étapes décrit l'approche par étapes de CTN Solutions.

Sources officielles et limites de lecture

Faits datés

Les pages officielles citées ont été lues le .

Vérifié le

Keycloak 26.8.2, publiée le 5 novembre 2026, est la dernière version stable à cette date. Les versions 26.7.3 et 26.8.2 corrigent des failles touchant les organisations : appliquez-les avant d'adopter ce modèle.

Un fournisseur d'identité réel comme Entra ID ou Okta peut se comporter autrement qu'un second realm Keycloak utilisé comme fournisseur.

Sources

  1. Téléchargements Keycloak, version stable 26.8.2 : https://www.keycloak.org/downloads
  2. Note de version Keycloak 26.8.2, 5 novembre 2026 : https://www.keycloak.org/2026/11/keycloak-2682-released
  3. Note de version Keycloak 26.8.0, 1er octobre 2026 : https://www.keycloak.org/2026/10/keycloak-2680-released
  4. Note de version Keycloak 26.7.3, 31 août 2026 : https://www.keycloak.org/2026/08/keycloak-2673-released
  5. Note de version Keycloak 26.7.0, 9 juillet 2026 : https://www.keycloak.org/2026/07/keycloak-2670-released
  6. Article Organization Groups, 29 avril 2026 : https://www.keycloak.org/2026/04/org-groups
  7. Note de version Keycloak 26.0.0, 4 octobre 2024 : https://www.keycloak.org/2024/10/keycloak-2600-released
  8. Guide d'administration Keycloak 26.8.2, chapitre Organizations : https://www.keycloak.org/docs/26.8.2/server_admin/
  9. API REST d'administration Keycloak : https://www.keycloak.org/docs-api/latest/rest-api/index.html

Parlons de votre contexte.

Cet article expose une pratique générale ; votre situation a ses propres contraintes. Décrivez-la, nous répondons avec un périmètre.

Ouvrir le formulaire