Aller au contenu

Configurer OIDC entre Keycloak et application interne

Configurer OIDC entre Keycloak et application interne

Section intitulée « Configurer OIDC entre Keycloak et application interne »

OpenID Connect (OIDC) délègue l’authentification à un fournisseur d’identité, puis transmet à l’application des preuves signées sur l’identité de la personne connectée. Le gain est centraliser les comptes, les politiques et l’authentification multifacteur ; le prix est une dépendance directe entre disponibilité, horloges, clés de signature et contrôle d’accès applicatif.

→ Lire l’explication complète de la marmotte

OIDC s’appuie sur OAuth 2.0, un protocole de délégation d’autorisation. Le fournisseur d’identité, ou IdP (Identity Provider), authentifie la personne ; l’application, appelée client OIDC, vérifie un jeton signé. L’application ne doit jamais déléguer sa décision d’autorisation au seul fait qu’un jeton existe. Un jeton prouve une identité dans un contexte donné, pas le droit d’effacer une base de données parce qu’un groupe porte un nom flatteur.

Retenir une architecture serveur avec le flux Authorization Code, ou code d’autorisation, complété par PKCE (Proof Key for Code Exchange). PKCE lie le code temporaire reçu par le navigateur à la session ayant initié la demande. Il faut l’activer même pour un client confidentiel, c’est-à-dire une application serveur qui possède un secret : les réseaux, les proxys et les navigateurs trouvent toujours une manière créative de rendre une protection facultative très facultative.

Le flux implicite est déprécié et doit rester désactivé. Il expose historiquement les jetons dans les fragments d’adresse, dans l’historique du navigateur ou dans des journaux frontaux. Le flux Resource Owner Password Credentials, qui collecte le mot de passe dans l’application, doit aussi être exclu : il annule la fédération et réintroduit une collecte locale de secrets.

Avant de créer le client, prévoir :

  • Keycloak 24 ou version ultérieure, ou un tenant Okta ou Microsoft Entra ID, anciennement Azure Active Directory, avec l’authentification multifacteur imposée par politique ;
  • un nom DNS stable pour l’application et un certificat Transport Layer Security (TLS) valide, voir les principes de PKI et TLS ;
  • une synchronisation horaire Network Time Protocol (NTP) sur les nœuds applicatifs, les proxys et l’IdP ;
  • un stockage serveur pour l’état de connexion, le paramètre state, le nonce OIDC et le vérificateur PKCE ;
  • une matrice d’autorisations interne, indépendante des noms de groupes bruts fournis par l’IdP.

L’IdP doit publier son document de découverte sous /.well-known/openid-configuration. L’application doit charger depuis ce document l’émetteur, les points d’accès d’autorisation et de jetons, ainsi que l’URL du jeu de clés publiques JSON Web Key Set (JWKS). Éviter les URL recopiées dans trois fichiers YAML : c’est une tradition de production qui mérite de disparaître avec les mots de passe dans les scripts Bash.

Déclarer le client OIDC et verrouiller les redirections

Section intitulée « Déclarer le client OIDC et verrouiller les redirections »
  1. Créer un client dédié par application et par environnement. Ne pas partager un identifiant client entre recette et production : une URI (Uniform Resource Identifier) de redirection de recette devient alors une porte latérale vers un domaine moins contrôlé.

  2. Choisir le type de client. Une application rendue côté serveur utilise un client confidentiel avec authentification du client au point d’accès de jetons. Une application monopage sans backend est un client public, sans secret embarqué, avec PKCE obligatoire. Un secret distribué dans JavaScript n’est pas un secret, c’est une décoration encodée en Base64.

  3. Enregistrer les URI de redirection exactes. Pour une application exposée sous https://portail.interne.example, n’autoriser que https://portail.interne.example/oidc/callback. Interdire les jokers, les préfixes et les redirections génériques telles que https://portail.interne.example/*. Une redirection permissive permet à un attaquant de récupérer un code d’autorisation sur une route contrôlée ou contournable.

  4. Déclarer aussi les URI de déconnexion post-connexion avec la même rigueur. La déconnexion fédérée ne doit rediriger que vers une page locale connue, jamais vers une valeur fournie dans une requête.

  5. Activer le flux standard, désactiver le flux implicite et les flux par mot de passe. Pour un client serveur, demander les portées openid, profile et email seulement si l’application les consomme. Chaque attribut personnel transporté sans usage est une dette de conformité, pas une décoration de jeton.

L’exemple suivant crée un client confidentiel dans le domaine Keycloak interne. La commande utilise kcadm.sh, fourni avec Keycloak, et suppose une session administrateur déjà ouverte. Les valeurs de production doivent venir d’un gestionnaire de secrets, pas d’un fichier .env abandonné entre deux répertoires temporaires.

Fenêtre de terminal
# Créer le client OIDC avec authorization code et PKCE S256.
kcadm.sh create clients -r interne \
-s clientId=portail-rh \
-s protocol=openid-connect \
-s enabled=true \
-s publicClient=false \
-s standardFlowEnabled=true \
-s implicitFlowEnabled=false \
-s directAccessGrantsEnabled=false \
-s 'redirectUris=["https://portail.interne.example/oidc/callback"]' \
-s 'webOrigins=["https://portail.interne.example"]' \
-s 'attributes={"pkce.code.challenge.method":"S256"}'
# Lire l'identifiant interne, puis exiger PKCE et récupérer le secret une seule fois.
CLIENT_ID=$(kcadm.sh get clients -r interne -q clientId=portail-rh \
| jq -r '.[0].id')
kcadm.sh update "clients/${CLIENT_ID}" -r interne \
-s 'attributes={"pkce.code.challenge.method":"S256"}'
kcadm.sh get "clients/${CLIENT_ID}/client-secret" -r interne

Dans Keycloak, ajouter un mappeur de rôles de domaine ou de client vers une revendication, ou claim, nommée par exemple roles. Limiter cette revendication aux rôles applicatifs. Exporter tout l’annuaire dans un jeton est coûteux, divulgue des informations et finit généralement en erreur de taille d’en-tête HTTP. Pour les groupes très nombreux, préférer une décision d’autorisation côté application à partir d’une API dédiée ou d’un référentiel local.

Valider le jeton d’identité et mapper les permissions

Section intitulée « Valider le jeton d’identité et mapper les permissions »

Après le retour sur l’URI de redirection, l’application vérifie state, échange le code contre des jetons, puis valide le JSON Web Token (JWT) d’identité. Cette validation est cryptographique et contextuelle. Décoder le jeton sans vérifier sa signature ne valide rien : cela lit une structure JSON contrôlée par le porteur, avec un habillage qui impressionne surtout les démonstrations commerciales.

La bibliothèque OIDC retenue doit vérifier, dans cet ordre logique :

  1. la signature avec une clé publique issue du JWKS de l’IdP, en limitant les algorithmes acceptés à ceux configurés, par exemple RS256 ou ES256 ;
  2. la revendication iss, ou issuer, strictement égale à l’émetteur découvert ;
  3. la revendication aud, ou audience, contenant l’identifiant du client ;
  4. exp, date d’expiration, et nbf, date de début de validité, avec une tolérance d’horloge maximale de 60 secondes ;
  5. le nonce associé à la session initiale, afin de bloquer la réutilisation d’un jeton injecté dans une autre session ;
  6. pour un jeton reçu par un client confidentiel, azp, ou authorized party, lorsqu’elle est présente et attendue.

Ne choisir jamais une permission applicative directement à partir d’un groupe IdP libre. Construire une table de correspondance versionnée : rh-lecteurs devient dossier.read, rh-gestionnaires devient dossier.write. Refuser par défaut tout rôle absent de cette table. Les groupes sont une information d’identité ; les permissions sont une décision locale. Mélanger les deux paraît économique jusqu’au premier renommage d’équipe.

Voir également les règles de séparation des identités et des droits sur la page Identités et les fondations d’architecture sur Architecture cyber.

Tester le parcours nominal, mais aussi les échecs qui surviennent hors des démonstrations :

  • modifier l’horloge applicative de plus de 60 secondes et vérifier le refus d’un jeton expiré ou pas encore valide ;
  • présenter un jeton signé par une autre clé, avec le bon émetteur et la bonne audience, puis vérifier le rejet ;
  • tenter une URI de redirection voisine, avec chemin supplémentaire, port différent, majuscule ou sous-domaine ;
  • retirer un rôle IdP, renouveler le jeton et vérifier que la permission disparaît ;
  • faire pivoter la clé de signature de l’IdP dans un environnement de validation.

Le cache JWKS doit respecter Cache-Control lorsque l’IdP le publie, avec une durée maximale bornée. En cas de kid inconnu, où kid est l’identifiant de clé dans l’en-tête du jeton, rafraîchir le JWKS une fois puis retenter la validation. Ne pas rafraîchir à chaque requête inconnue : un attaquant peut sinon transformer un jeton forgé en générateur de trafic vers l’IdP. Ne pas attendre non plus l’expiration d’un cache de plusieurs heures : après une rotation, les nouvelles connexions échouent alors que le jeton est parfaitement valide.

Faire tourner le secret du client confidentiel avec chevauchement contrôlé si l’IdP le permet. Journaliser l’identifiant client, l’émetteur, l’audience, le kid et le motif de refus, jamais le jeton complet ni le code d’autorisation. Un jeton dans un journal est un badge d’accès posé dans un bac de recyclage, avec rétention légale en prime.

OIDC ne garantit ni la révocation instantanée des jetons déjà émis, ni l’absence de session locale, ni la disponibilité de l’IdP. Limiter la durée des jetons, imposer une réauthentification selon la sensibilité et invalider localement les sessions lors d’un retrait d’accès critique. Sauvegarder la configuration de l’IdP, les mappages et les métadonnées de client ; ne jamais sauvegarder un secret de client en clair dans le même dépôt. La reprise exige aussi la restauration des clés de signature ou leur continuité : changer d’émetteur ou perdre les clés invalide les sessions existantes, ce qui est parfois préférable à une illusion de continuité.

Sans fédération OIDC, chaque application conserve sa propre base de comptes : l’authentification multifacteur devient une option locale, rarement homogène, et les mots de passe circulent dans autant de formulaires que de services, ce qui rend l’hameçonnage difficile à distinguer d’une page légitime. Les départs et changements de poste ne se propagent plus : un compte applicatif oublié survit à la révocation de l’annuaire, sans qu’aucun inventaire central ne le signale. La traçabilité se fragmente aussi, puisqu’il n’existe plus d’identifiant d’émetteur ni d’audience communs pour corréler une même personne entre journaux applicatifs, ce qui allonge toute investigation. Enfin, les politiques transverses (durée de session, réauthentification pour les actions sensibles, blocage après tentatives) doivent être réécrites service par service, avec des écarts que rien ne détecte tant qu’un accès n’a pas été utilisé.

L’application ne vérifie pas elle-même le mot de passe de la personne. Elle envoie la personne vers un fournisseur d’identité, un service chargé de vérifier son compte et, si nécessaire, son second facteur de connexion. L’application enregistre avant ce départ une valeur aléatoire liée à la session et une preuve temporaire. Ces valeurs empêchent qu’une réponse obtenue pour une autre session soit réutilisée.

Après la connexion, le fournisseur renvoie la personne vers une adresse précise de l’application avec un code court. L’application échange ce code contre un document signé contenant l’identité, sa date d’expiration et le nom de l’application concernée. Elle vérifie la signature avec une clé publique publiée par le fournisseur, puis vérifie que le document vient bien du fournisseur attendu, vise bien cette application et n’est pas expiré.

L’application transforme ensuite les groupes reçus en droits locaux, par exemple lecture ou modification. Un groupe inconnu ne donne aucun droit. Si l’horloge du serveur est décalée, un document encore valable peut être refusé. Si le fournisseur change sa clé de signature, l’application doit récupérer la nouvelle clé publique. Sans ce renouvellement, les nouvelles connexions échouent. Une personne déjà connectée peut conserver ses droits jusqu’à l’expiration de sa session locale : retirer un groupe ne coupe donc pas toujours l’accès immédiatement.

À lire aussi

Pages voisines