Le modèle de Keycloak

Les objets de Keycloak (realm, client, utilisateur, groupe, rôles, client scopes, protocol mappers, user profile) et la façon dont ils décident du contenu des jetons.

Keycloak essentiel Cours 5 sur 17 de Keycloak Academy 35 min Suppose OIDC et SAML : voir les fondations 5 questions de quiz Cours suivant : Le découpage en realms

Sur cette page
  1. Le realm, unité d’isolement
  2. Les clients : les applications vues par Keycloak
  3. Utilisateurs, groupes et rôles
  4. Ce qui part dans le jeton : client scopes et protocol mappers
  5. Le user profile : déclarer les attributs
  6. Console d’administration et console de compte
  7. Ce qu’il faut retenir pour l’architecture
  8. Natif, extension ou hébergeur
  9. Quiz

Vous connaissez OIDC et SAML. Keycloak les implémente avec un modèle d’objets qui lui est propre, et c’est ce modèle que vous manipulerez à chaque décision : où vit un utilisateur, comment un droit est rangé, ce qui décide qu’une information part ou non dans un jeton. Ce cours pose ce vocabulaire. Les cours suivants s’appuient dessus pour trancher le découpage en realms, les sources d’utilisateurs et les parcours d’authentification.

Le realm, unité d’isolement

Un realm est un espace d’identité autonome. Il contient ses utilisateurs, ses clients, ses rôles, ses groupes, ses parcours d’authentification, ses clés de signature, sa politique de mots de passe et son thème. Un utilisateur appartient à un seul realm et une session SSO ne vaut que dans son realm.

Chaque realm expose ses propres points de terminaison. L’émetteur (iss) des jetons est l’URL du realm, par exemple https://idp.example.org/realms/agents. Une application raccordée à un realm ne voit rien des autres.

Le realm master est particulier : il sert à administrer l’instance et les autres realms. On n’y met pas d’utilisateurs métier. Le choix du nombre de realms est traité dans le cours suivant.

Les clients : les applications vues par Keycloak

Un client est une application (ou une API) qui délègue l’authentification à Keycloak. Il a un protocole, OIDC ou SAML, et un identifiant (clientId en OIDC, l’entity ID en SAML).

En OIDC, un client est soit public (application monopage, application mobile : il ne peut pas garder de secret), soit confidentiel (application serveur, qui s’authentifie auprès de Keycloak par secret, clé privée signée ou certificat). Un client confidentiel peut aussi disposer d’un service account, un compte technique utilisé avec le flux client credentials.

Le client porte ses propres réglages : URI de redirection autorisées, durées de session spécifiques, thème de connexion, surcharge de parcours d’authentification, niveau d’authentification minimal. Ces réglages par client évitent souvent de créer un realm, comme on le verra au cours 2.

Utilisateurs, groupes et rôles

Un utilisateur a un identifiant technique (UUID, qui devient le sub des jetons OIDC), un nom d’utilisateur, un e-mail, des attributs et des credentials (mot de passe, OTP, clé WebAuthn).

Les rôles expriment des droits :

  • un rôle de realm a un sens pour tout le realm (par exemple agent, usager) ;
  • un rôle de client n’a de sens que pour une application (par exemple editeur sur le client portail-agents) ;
  • un rôle composite contient d’autres rôles, de realm ou de client. Affecter le composite revient à affecter tout ce qu’il contient.

Les groupes sont hiérarchiques. Un utilisateur membre d’un groupe hérite des rôles affectés au groupe et à ses groupes parents. Les groupes peuvent aussi porter des attributs, que certains mappers et conditions savent lire. Les groupes servent à organiser des populations (une direction, un service, un partenaire) et à porter des rôles en masse.

ObjetPortéeUsage typiqueDans le jeton OIDC par défaut
Rôle de realmtout le realmprofil transverse : agent, usager, administrateurrealm_access.roles
Rôle de clientun clientdroit applicatif : lecteur, éditeur, valideurresource_access.<client>.roles
Rôle compositeselon ses composantsprofil métier qui regroupe plusieurs droitsles rôles effectifs, dépliés
Groupehiérarchie du realmstructure organisationnelle, affectation en masseabsent sans mapper dédié
Attribut utilisateurun utilisateurdonnée d’identité : matricule, serviceabsent sans mapper dédié

Chaque realm a aussi un rôle composite default-roles-<realm>, affecté à tout nouvel utilisateur. Le détail du RBAC et de l’héritage par les groupes fait l’objet du cours 5.

Ce qui part dans le jeton : client scopes et protocol mappers

Un protocol mapper transforme une donnée du modèle (rôle, attribut, groupe, propriété de session) en claim OIDC ou en attribut SAML. Un client scope regroupe des mappers et des rôles sous un nom réutilisable : profile, email, roles, acr, ou les vôtres.

Un client scope est rattaché à un client de deux façons :

  • default : appliqué à chaque demande de jeton du client ;
  • optional : appliqué seulement si l’application le demande dans le paramètre scope de la requête OIDC.

Deux réglages limitent ensuite les rôles inclus :

  • Full scope allowed (actif par défaut) place dans le jeton tous les rôles de l’utilisateur, quel que soit le client ;
  • une fois ce réglage désactivé, les role scope mappings du client et de ses client scopes définissent les rôles autorisés à apparaître.

Voici un access token produit pour un utilisateur qui a le rôle de realm composite agent (contenant le rôle de client lecteur) et qui appartient au groupe instructeurs (qui porte le rôle de client editeur), avec un client scope optional agent-info demandé :

{
  "iss": "http://localhost:8080/realms/academy",
  "aud": "account",
  "sub": "f1fd4eab-20fb-4c3b-b814-b733d1a81b70",
  "azp": "portail-agents",
  "acr": "1",
  "scope": "openid agent-info profile email",
  "realm_access": { "roles": ["agent", "offline_access", "default-roles-academy", "uma_authorization"] },
  "resource_access": {
    "portail-agents": { "roles": ["lecteur", "editeur"] },
    "account": { "roles": ["manage-account", "manage-account-links", "view-profile"] }
  },
  "matricule": "A123456",
  "preferred_username": "jdupont",
  "email": "jean.dupont@example.org"
}

Le rôle lecteur vient du composite, editeur du groupe, matricule du mapper porté par le client scope. Si l’application n’avait pas demandé agent-info, le claim matricule serait absent.

Le mapper se crée ainsi :

kcadm.sh create client-scopes/$SCOPE_ID/protocol-mappers/models -r academy \
  -s name=matricule -s protocol=openid-connect \
  -s protocolMapper=oidc-usermodel-attribute-mapper \
  -s 'config."user.attribute"=matricule' -s 'config."claim.name"=matricule' \
  -s 'config."jsonType.label"=String' \
  -s 'config."access.token.claim"=true' -s 'config."id.token.claim"=true'

Pour un besoin que les mappers fournis ne couvrent pas (un claim calculé à partir d’un référentiel externe, par exemple), il faut écrire un protocol mapper en Java. C’est une extension, avec son coût de maintenance à chaque montée de version.

Le user profile : déclarer les attributs

Depuis Keycloak 24, le user profile est supporté et activé par défaut. Chaque realm déclare en JSON les attributs qu’il gère. Pour chacun :

  • des validations : longueur, motif (expression régulière), e-mail, URI, liste de valeurs, date, entier, etc. ;
  • des permissions : qui peut le voir (view) et le modifier (edit), parmi admin et user ;
  • une condition required : obligatoire pour les administrateurs, pour l’usager, ou seulement quand un client scope donné est demandé ;
  • des annotations pour l’affichage (type de champ, aide) et un groupe d’attributs pour structurer les formulaires.
{
  "name": "matricule",
  "displayName": "Matricule",
  "validations": {
    "pattern": {
      "pattern": "^[A-Z][0-9]{6}$",
      "error-message": "Format attendu : une lettre et six chiffres"
    }
  },
  "permissions": { "view": ["admin", "user"], "edit": ["admin"] },
  "multivalued": false
}

Ces règles s’appliquent partout : console d’administration, API REST, console de compte, formulaires d’inscription et de mise à jour du profil. Un usager ne peut pas contourner une permission edit: admin en appelant l’API de compte.

Les attributs non déclarés sont des attributs non gérés. Par défaut, ils sont refusés. La politique du realm peut les autoriser partout, ou seulement en lecture ou en écriture pour les administrateurs. Une migration depuis une version antérieure à 24 est le cas typique où cette politique compte : des attributs posés par des scripts existants peuvent devenir invisibles ou être rejetés.

Console d’administration et console de compte

La console d’administration (/admin/<realm>/console) permet de tout configurer. Les droits d’administration sont des rôles du client realm-management de chaque realm : manage-users, view-users, manage-clients, view-events, etc. On peut donc donner à une équipe la gestion des utilisateurs d’un realm sans lui ouvrir le reste. Les permissions fines (déléguer la gestion d’un groupe seulement) sont traitées dans le cours 8.

La console de compte (/realms/<realm>/account) est ce que voit l’usager :

RubriqueCe que l’usager faitCe qui la pilote
Informations personnellesvoir et modifier ses attributspermissions du user profile
Connexionchanger son mot de passe, ajouter ou retirer un OTP, une clé WebAuthnrequired actions activées, parcours d’authentification
Activité des appareilsvoir ses sessions, se déconnecter à distancesessions du realm
Comptes liéslier ou délier un fournisseur d’identité externeidentity providers du realm
Applicationsvoir les applications utilisées, révoquer un consentementclients et consentements

La suppression de compte par l’usager existe mais est désactivée par défaut : il faut activer la required action Delete Account et donner le rôle delete-account.

Ce qu’il faut retenir pour l’architecture

Trois questions reviennent sans cesse dans un projet Keycloak, et le modèle y répond :

  1. Où est rangé ce droit ? Rôle de realm s’il est transverse, rôle de client s’il est applicatif, porté par un groupe s’il suit l’organisation.
  2. Qui a besoin de le voir dans un jeton ? Un client scope, default ou optional, et Full scope allowed désactivé.
  3. D’où vient cette donnée et qui la modifie ? Le user profile pour les règles, la fédération pour la source (cours 3).

Construire le modèle et lire le jeton

25 min

Installer le lab
  1. Depuis le dossier keycloak-academy-lab/, ouvrez une session kcadm dans le conteneur Keycloak. Les commandes suivantes supposent la fonction shell définie ici.
kcadm() { docker compose exec -T keycloak /opt/keycloak/bin/kcadm.sh "$@"; }
kcadm config credentials --server http://localhost:8080 --realm master --user admin --password admin
  1. Créez le realm academy (s’il existe déjà, l’erreur est sans conséquence), un client confidentiel et ses rôles.
kcadm create realms -s realm=academy -s enabled=true
CID=$(kcadm create clients -r academy -s clientId=portail-agents -s publicClient=false \
  -s 'redirectUris=["http://localhost:3000/*"]' -i)
kcadm create roles -r academy -s name=agent
kcadm create clients/$CID/roles -r academy -s name=lecteur
kcadm create clients/$CID/roles -r academy -s name=editeur
kcadm add-roles -r academy --rname agent --cclientid portail-agents --rolename lecteur
  1. Créez un groupe instructeurs portant le rôle editeur, puis un utilisateur membre du groupe avec le rôle agent.
GID=$(kcadm create groups -r academy -s name=instructeurs -i)
kcadm add-roles -r academy --gid $GID --cclientid portail-agents --rolename editeur
USER_ID=$(kcadm create users -r academy -s username=jdupont -s email=jean.dupont@example.org \
  -s firstName=Jean -s lastName=Dupont -s enabled=true -i)
kcadm update users/$USER_ID/groups/$GID -r academy -n
kcadm add-roles -r academy --uusername jdupont --rolename agent
  1. Générez un access token d’exemple sans passer par une connexion, et repérez d’où vient chaque rôle.
kcadm get clients/$CID/evaluate-scopes/generate-example-access-token -r academy \
  -q scope=openid -q userId=$USER_ID
  1. Dans la console (http://localhost:8080/admin, realm academy), ouvrez Realm settings, onglet User profile, et créez l’attribut matricule avec un validateur pattern ^[A-Z][0-9]{6}$, visible par admin et user, modifiable par admin seulement.
  2. Vérifiez la validation : la première commande doit échouer avec votre message d’erreur, la seconde réussir.
kcadm update users/$USER_ID -r academy -s 'attributes.matricule=["X12"]'
kcadm update users/$USER_ID -r academy -s 'attributes.matricule=["A123456"]'
  1. Créez un client scope agent-info, ajoutez-lui le mapper matricule (commande de la section sur les mappers, avec $SCOPE_ID), rattachez-le en optional au client, puis comparez les jetons avec et sans ce scope.
SCOPE_ID=$(kcadm create client-scopes -r academy -s name=agent-info -s protocol=openid-connect -i)
# créer ici le mapper matricule sur client-scopes/$SCOPE_ID
kcadm update clients/$CID/optional-client-scopes/$SCOPE_ID -r academy
kcadm get clients/$CID/evaluate-scopes/generate-example-access-token -r academy \
  -q "scope=openid agent-info" -q userId=$USER_ID
  1. Désactivez Full scope allowed sur portail-agents (dans la console : onglet Client scopes du client, scope dédié, onglet Scope), puis régénérez le jeton. realm_access et les rôles du client account disparaissent ; seuls restent les rôles du client lui-même.
kcadm update clients/$CID -r academy -s fullScopeAllowed=false

Natif, extension ou hébergeur

Natif : fourni par Keycloak, à configurer. Extension : demande du code à maintenir (un SPI, le mécanisme de plugin de Keycloak). Hébergeur : dépend de la plateforme qui héberge Keycloak, à vérifier avec elle.

CapacitéVerdictPrécision
Realms, clients, utilisateurs, groupes, rôles de realm et de client, rôles compositesNatifSocle du produit, administrable par console, API REST et kcadm.sh.
Ajout de rôles, groupes et attributs dans les jetons via client scopes et protocol mappers fournisNatifLes mappers OIDC et SAML fournis couvrent les besoins courants (attribut, rôle, groupe, audience, valeur fixe).
Claim calculé par une logique métier (appel à un référentiel, règle complexe)ExtensionDemande un ProtocolMapper écrit en Java et déployé comme provider. Le mapper JavaScript existe, mais il dépend d'une fonctionnalité à activer et doit lui aussi être packagé et déployé comme provider.
User profile déclaratif (attributs, validations, droits de lecture et d'écriture)NatifActivé par défaut et supporté depuis Keycloak 24, configuré en JSON par realm.
Validateur d'attribut spécifique (contrôle de clé, appel externe)ExtensionLes validateurs fournis (longueur, motif, e-mail, URI, liste de valeurs, date…) couvrent la plupart des cas ; au-delà, un Validator SPI.
Console de compte (profil, mots de passe, facteurs, sessions, comptes liés)NatifPilotée par le user profile et les required actions. Une refonte fonctionnelle demande un thème ou une extension.
Accès au realm master et aux droits d'administration globauxHébergeurChez un hébergeur, l'accès au realm master et aux comptes d'administration est souvent restreint : à vérifier avec lui.

Vérifier ce que vous avez retenu

5 questions

  1. Une application métier a besoin de connaître le rôle « valideur » de l'utilisateur, qui n'a de sens que pour elle. Où le déclarer ?

  2. Un client reçoit dans son access token tous les rôles de tous les clients du realm. Quelle est la cause la plus probable ?

  3. Vous voulez qu'un claim matricule n'apparaisse que pour les applications qui le demandent explicitement. Quel montage choisir ?

  4. Un usager ne doit jamais pouvoir modifier son numéro d'agent dans la console de compte, mais doit pouvoir le voir. Que configurer ?

  5. Un chef de projet vous demande d'ajouter « juste un attribut » dans le jeton. Quelle question posez-vous en premier ?