Le raccordement des applications
Choisir le bon type de client et le bon flux OIDC, authentifier les clients, configurer un fournisseur de services SAML, traiter la déconnexion et raccorder par reverse proxy les applications qui ne parlent ni OIDC ni SAML.
Sur cette page
Raccorder une application, c’est choisir un protocole, un type de client, un flux, une façon d’authentifier le client et une stratégie de déconnexion. Ces choix se répètent pour chaque application du périmètre : ils méritent une grille plutôt qu’une décision au cas par cas. Ce cours couvre OIDC, SAML, la déconnexion et le cas des applications qui ne parlent aucun des deux. Les flux OAuth 2.0 et les échanges SAML eux-mêmes sont supposés connus (voir les fondations).
Client public ou confidentiel
Dans Keycloak, un client OIDC est confidentiel quand l’option « Client authentication » est activée : il doit s’authentifier pour obtenir des jetons. Il est public quand elle est désactivée : application monopage, application mobile ou native, qui ne peut pas garder de secret.
Les flux s’activent case par case dans « Capability config » :
| Option de la console | Flux | Quand l’activer |
|---|---|---|
| Standard flow | Code d’autorisation (avec PKCE) | Toute application avec un utilisateur, web ou mobile |
| Service accounts roles | Client credentials | Appel machine à machine, sans utilisateur |
| OAuth 2.0 Device Authorization Grant | Device flow | Équipement sans clavier ni navigateur |
| Direct access grants | Grant password | Jamais en production ; RFC 9700 l’interdit |
| Implicit flow | Implicit | Jamais ; remplacé par code + PKCE |
Désactivez tout ce qui ne sert pas. Les valeurs par défaut diffèrent selon que le client est créé dans la console ou par l’API : vérifiez-les plutôt que de les supposer. Les client policies (executors reject-ropc-grant, reject-implicit-grant) permettent de les interdire pour tout un realm.
Code d’autorisation avec PKCE
PKCE lie la demande d’autorisation à l’échange du code : le client envoie code_challenge (empreinte SHA-256 d’un secret aléatoire), puis présente ce secret (code_verifier) au token endpoint. Un code intercepté devient inutilisable.
Keycloak accepte PKCE dès que le client l’envoie. Pour l’exiger, réglez « PKCE Method » à S256 dans les paramètres avancés du client, ou appliquez l’executor pkce-enforcer par client policy. Sans code_challenge, Keycloak renvoie alors l’erreur suivante vers la redirect URI :
http://localhost:3000/callback?error=invalid_request&error_description=Missing+parameter%3A+code_challenge_method
RFC 9700 recommande PKCE pour tous les clients, confidentiels compris. Ajoutez aux exigences du guide d’intégration : redirect URIs exactes (pas de joker en production), state ou PKCE contre le CSRF, nonce pour le jeton d’identité.
kcadm.sh create clients -r agents -s clientId=portail-usagers -s publicClient=true \
-s 'redirectUris=["https://portail.example.org/callback"]' \
-s 'webOrigins=["https://portail.example.org"]' \
-s 'attributes."pkce.code.challenge.method"=S256' \
-s directAccessGrantsEnabled=false
Client credentials
Le flux client credentials sert quand une application agit en son nom propre. Keycloak crée pour le client un service account, un utilisateur technique nommé service-account-<clientId>. On attribue des rôles à ce compte comme à un utilisateur (onglet « Service accounts roles »), et ils apparaissent dans le jeton :
{
"iss": "http://localhost:8080/realms/lab-apps",
"aud": "account",
"sub": "c9095298-80f4-4df7-9a39-dfaf5813ef5a",
"azp": "batch-export",
"realm_access": { "roles": ["offline_access", "uma_authorization", "default-roles-lab-apps"] },
"scope": "email profile",
"preferred_username": "service-account-batch-export",
"client_id": "batch-export",
"clientHost": "10.20.8.1",
"clientAddress": "10.20.8.1"
}
Par défaut, Keycloak ne délivre pas de refresh token dans ce flux : le client redemande un jeton à expiration. Retirez les rôles par défaut inutiles du service account et appliquez le moindre privilège, surtout s’il appelle l’Admin REST API (sujet traité dans le cours sur l’API d’administration).
Authentifier le client
Un client confidentiel prouve son identité au token endpoint. Keycloak propose quatre client authenticators (onglet Credentials, champ « Client Authenticator ») :
| Authenticator (console) | Méthode OIDC | Secret détenu par | Points d’attention |
|---|---|---|---|
| Client Id and Secret | client_secret_basic, client_secret_post | Les deux parties | Simple ; secret à stocker côté application et à faire tourner |
| Signed Jwt | private_key_jwt | Le client seul (clé privée) | Clé publique fournie par URL JWKS (« Use JWKS URL ») ou certificat importé ; rotation sans échange de secret |
| Signed Jwt with Client Secret | client_secret_jwt | Les deux parties | Le secret ne transite pas, mais reste partagé ; peu d’intérêt face à Signed Jwt |
| X509 Certificate | tls_client_auth (mTLS) | Le client seul (certificat) | Keycloak doit voir le certificat client : TLS terminé sur Keycloak avec authentification client, ou proxy qui transmet le certificat |
RFC 9700 privilégie les méthodes asymétriques (private_key_jwt, mTLS). Le choix dépend surtout de ce que l’équipe applicative sait opérer :
# Signed Jwt avec clé publique publiée par l'application
kcadm.sh update clients/$ID -r agents -s clientAuthenticatorType=client-jwt \
-s 'attributes."use.jwks.url"=true' \
-s 'attributes."jwks.url"=https://batch.example.org/.well-known/jwks.json'
# X509 : Keycloak vérifie le DN du certificat présenté
kcadm.sh update clients/$ID -r agents -s clientAuthenticatorType=client-x509 \
-s 'attributes."x509.subjectdn"=CN=batch-export,OU=Applications,O=Exemple'
SAML : métadonnées, signature, NameID, mappers
Pour SAML, Keycloak est l’IdP. Le client SAML a pour identifiant l’entityID du fournisseur de services (SP).
Métadonnées. Le SP récupère les métadonnées de l’IdP à l’adresse /realms/<realm>/protocol/saml/descriptor : entityID, certificat de signature, endpoints SSO et SLO. Dans l’autre sens, la console importe le fichier de métadonnées du SP (Clients, « Import client ») et en déduit l’ACS, les URLs de logout et le certificat.
Signature et chiffrement. Les options principales du client :
- « Sign documents » signe la réponse SAML entière ; « Sign assertions » signe l’assertion. Activer les deux est fréquent ; certains SP n’acceptent que l’un des deux, à tester.
- « Signature algorithm » : RSA_SHA256 au minimum.
- « Client signature required » : Keycloak exige que les requêtes du SP soient signées et vérifie la signature avec le certificat du SP (onglet Keys).
- « Encrypt assertions » : l’assertion est chiffrée avec la clé publique du SP. Utile si l’assertion transite par des composants non maîtrisés ; sinon le TLS suffit souvent.
NameID. Le champ « Name ID format » propose username, email, transient et persistent. « Force name ID format » ignore le format demandé par le SP. Préférez persistent pour un identifiant stable et opaque ; email est pratique mais change quand l’adresse change, ce qui casse le rattachement au compte applicatif.
Mappers. Les attributs de l’assertion sont produits par des mappers SAML : User Property (email, firstName), User Attribute, Role list, Group list, Hardcoded attribute. Chacun fixe le nom de l’attribut et son « Name format » (Basic, URI Reference, Unspecified). Le SP impose souvent des noms précis (mail, ou un OID urn:oid:0.9.2342.19200300.100.1.3) : relevez-les dans la fiche de l’application.
<saml:Attribute Name="mail" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic">
<saml:AttributeValue>alice@academy.local</saml:AttributeValue>
</saml:Attribute>
La déconnexion : ce qui marche réellement
La déconnexion est le point où les promesses des éditeurs et la réalité divergent le plus. Il faut distinguer deux questions : l’application sait-elle demander une déconnexion, et sait-elle recevoir une déconnexion déclenchée ailleurs ?
Demander. En OIDC, l’application redirige vers l’end_session_endpoint avec id_token_hint (ou client_id) et post_logout_redirect_uri, qui doit être déclarée dans le client (« Valid post logout redirect URIs »). Keycloak ferme la session SSO puis notifie les autres clients.
Recevoir. Keycloak notifie chaque client de la session selon sa configuration :
- Front channel logout activé : Keycloak affiche une page qui charge en iframe la « Front-channel logout URL » de chaque client. Simple, mais dépend du navigateur : le blocage des cookies tiers empêche souvent l’application de retrouver sa session dans l’iframe.
- Front channel logout désactivé et « Backchannel logout URL » renseignée : Keycloak envoie un
logout_token(JWT signé) de serveur à serveur. Fiable, mais l’application doit être joignable par Keycloak et savoir retrouver sa session locale à partir dusid(« Backchannel logout session required ») ou dusub.
Selon la façon dont le client est créé (console ou API), la valeur par défaut de « Front channel logout » diffère : dans le lab, un client créé par kcadm.sh l’a désactivée. Fixez-la explicitement.
En SAML, le SLO passe par les URLs « Logout Service » du client, en binding Redirect ou POST (par le navigateur) ou SOAP (de serveur à serveur), selon ce que le SP déclare dans ses métadonnées.
| Mécanisme | Prérequis côté application | Fiabilité observée |
|---|---|---|
| OIDC back-channel | URL joignable depuis Keycloak, gestion du sid | Bonne quand c’est implémenté ; c’est rarement le cas des applications anciennes |
| OIDC front-channel | Page de logout chargeable en iframe | Fragile avec les politiques de cookies des navigateurs |
| SAML SLO front (Redirect/POST) | Endpoint SLO, signature des messages | Variable ; une application qui échoue peut interrompre la chaîne |
| SAML SLO SOAP | Endpoint SOAP joignable | Rarement implémenté côté SP |
| Aucun | — | La session applicative vit jusqu’à son expiration : réduire sa durée |
Pour chaque application, la fiche doit dire ce qui se passe quand l’utilisateur se déconnecte ailleurs. « Rien jusqu’à expiration de la session locale » est une réponse acceptable si elle est connue et que la durée est courte.
Applications sans OIDC ni SAML : le reverse proxy
Une application qui ne sait lire qu’un utilisateur dans un en-tête HTTP (REMOTE_USER, X-Forwarded-User) peut être placée derrière un reverse proxy qui fait le travail OIDC. Pour Keycloak, le proxy est un client confidentiel ordinaire.
Avec mod_auth_openidc (module Apache) :
OIDCProviderMetadataURL https://sso.example.org/realms/agents/.well-known/openid-configuration
OIDCClientID appli-legacy
OIDCClientSecret "exec:/usr/local/bin/lire-secret appli-legacy"
OIDCRedirectURI https://appli.example.org/redirect_uri
OIDCCryptoPassphrase "exec:/usr/local/bin/lire-secret passphrase"
OIDCPKCEMethod S256
OIDCRemoteUserClaim preferred_username
OIDCPassClaimsAs headers
<Location />
AuthType openid-connect
Require claim roles:utilisateur
</Location>
Ici, un mapper « User Client Role » du client appli-legacy écrit ses rôles dans un claim plat roles, plus simple à tester qu’un chemin imbriqué. D’après sa documentation, mod_auth_openidc accepte aussi le back-channel logout sur son redirect URI (https://appli.example.org/redirect_uri?logout=backchannel), à renseigner comme « Backchannel logout URL » du client.
Avec oauth2-proxy (provider keycloak-oidc) :
oauth2-proxy \
--provider=keycloak-oidc \
--oidc-issuer-url=https://sso.example.org/realms/agents \
--client-id=appli-legacy --client-secret-file=/run/secrets/appli-legacy \
--redirect-url=https://appli.example.org/oauth2/callback \
--code-challenge-method=S256 \
--allowed-role=appli-legacy:utilisateur \
--email-domain='*' \
--upstream=http://127.0.0.1:8081
oauth2-proxy vérifie que son client-id figure dans aud : ajoutez un mapper Audience au client (voir le cours sur les jetons).
Les limites du modèle, à écrire noir sur blanc dans le guide d’intégration :
- Confiance dans l’en-tête. L’application croit tout en-tête reçu. Elle ne doit être joignable que par le proxy (réseau, pare-feu, écoute sur 127.0.0.1), et le proxy doit écraser les en-têtes entrants du même nom.
- Autorisation grossière. Le proxy filtre l’accès ; les droits fins restent dans l’application, souvent dans sa propre base de comptes, qu’il faut alors provisionner.
- Déconnexion. Le proxy ferme sa propre session ; si l’application garde une session à elle, elle ne le sait pas.
- API et clients lourds. Un client non navigateur (client lourd, script) ne suit pas les redirections OIDC.
Grille de catégorisation
| Catégorie | Profil de l’application | Exemple de travail |
|---|---|---|
| Simple | Support natif OIDC ou SAML configurable, comptes créés au premier login | Créer le client, renseigner la fiche, tester login et logout |
| Moyenne | Protocole standard mais rôles, attributs ou NameID spécifiques ; base de comptes locale à rapprocher ; déconnexion partielle | Mappers dédiés, rapprochement des comptes, tests de SLO |
| Complexe | Ni OIDC ni SAML (reverse proxy), mTLS, protocole propriétaire, ou autorisations à refondre | Proxy, cloisonnement réseau, éventuelle extension, recette de sécurité |
Raccorder une SPA, un batch et un SP SAML
35 min
Installer le lab- Dans
keycloak-academy-lab/, préparez kcadm.sh et créez le realm et un utilisateur :
KC="docker compose exec -T keycloak /opt/keycloak/bin/kcadm.sh"
$KC config credentials --server http://localhost:8080 --realm master --user admin --password admin
$KC create realms -s realm=lab-apps -s enabled=true
$KC create users -r lab-apps -s username=alice -s enabled=true -s email=alice@academy.local \
-s firstName=Alice -s lastName=Martin -s emailVerified=true
$KC set-password -r lab-apps --username alice --new-password password- Créez un client public qui exige PKCE S256 :
$KC create clients -r lab-apps -s clientId=spa-demo -s publicClient=true \
-s 'redirectUris=["http://localhost:3000/callback"]' -s 'webOrigins=["http://localhost:3000"]' \
-s 'attributes."pkce.code.challenge.method"=S256'- Ouvrez dans un navigateur l’URL d’autorisation sans PKCE et constatez l’erreur
Missing parameter: code_challenge_methoddans la barre d’adresse :
http://localhost:8080/realms/lab-apps/protocol/openid-connect/auth?client_id=spa-demo&response_type=code&scope=openid&redirect_uri=http://localhost:3000/callback- Générez un couple verifier/challenge et affichez l’URL avec PKCE :
VERIFIER=$(openssl rand -base64 48 | tr -d '=+/\n' | cut -c1-64)
CHALLENGE=$(printf %s "$VERIFIER" | openssl dgst -sha256 -binary | openssl base64 -A | tr '+/' '-_' | tr -d '=')
echo "http://localhost:8080/realms/lab-apps/protocol/openid-connect/auth?client_id=spa-demo&response_type=code&scope=openid&redirect_uri=http://localhost:3000/callback&state=xyz&code_challenge=$CHALLENGE&code_challenge_method=S256"- Ouvrez cette URL, connectez-vous avec alice / password. La page
localhost:3000ne répond pas, c’est normal : copiez la valeurcodede la barre d’adresse et échangez-la dans la minute :
curl -s http://localhost:8080/realms/lab-apps/protocol/openid-connect/token \
-d grant_type=authorization_code -d client_id=spa-demo -d redirect_uri=http://localhost:3000/callback \
-d code_verifier=$VERIFIER -d code=<code copié> | python3 -m json.tool- Créez un client batch en client credentials, obtenez un jeton, puis décodez-le localement avec la commande python3 du cours précédent (évitez de coller des jetons dans un site tiers). Vérifiez l’absence de refresh token et le nom du service account :
$KC create clients -r lab-apps -s clientId=batch-export -s secret=batch-secret \
-s standardFlowEnabled=false -s directAccessGrantsEnabled=false -s serviceAccountsEnabled=true
curl -s http://localhost:8080/realms/lab-apps/protocol/openid-connect/token \
-u batch-export:batch-secret -d grant_type=client_credentials | python3 -m json.tool-
Dans la console, ouvrez
batch-export, onglet Credentials, et parcourez la liste « Client Authenticator ». Passez sur « Signed Jwt » sans enregistrer et notez ce que Keycloak demande (clé ou URL JWKS). -
Créez un fournisseur de services SAML fictif avec un NameID forcé à l’e-mail et un mapper d’attribut, puis téléchargez les métadonnées de l’IdP :
$KC create clients -r lab-apps -s clientId=https://sp.academy.local/saml -s protocol=saml \
-s 'redirectUris=["https://sp.academy.local/*"]' \
-s 'attributes."saml_assertion_consumer_url_post"=https://sp.academy.local/saml/acs' \
-s 'attributes."saml_name_id_format"=email' -s 'attributes."saml.force.name.id.format"=true'
SP=$($KC get clients -r lab-apps -q clientId=https://sp.academy.local/saml --fields id --format csv --noquotes)
$KC create clients/$SP/protocol-mappers/models -r lab-apps -s name=mail -s protocol=saml \
-s protocolMapper=saml-user-property-mapper -s 'config."user.attribute"=email' \
-s 'config."attribute.name"=mail' -s 'config."attribute.nameformat"=Basic'
curl -s http://localhost:8080/realms/lab-apps/protocol/saml/descriptor-
Dans la console, ouvrez le client SAML : repérez « Sign documents », « Sign assertions », « Client signature required » (onglet Keys) et les URLs « Logout Service » (Advanced). Puis ouvrez
spa-demo, section « Logout settings », et notez l’état de « Front channel logout ». -
Rédigez la fiche de raccordement de
batch-exporten cinq lignes : protocole, flux, authentification du client, rôles attendus, comportement à la déconnexion.
À retenir
- Client public + code + PKCE pour tout ce qui a un utilisateur ; client credentials pour le machine à machine ; ni implicit ni grant password.
- Préférez une authentification de client asymétrique (Signed Jwt) quand l’équipe sait l’opérer ; le mTLS est aussi un sujet d’architecture réseau.
- En SAML, relevez dans la fiche : entityID, ACS, signature attendue, format du NameID, noms d’attributs.
- La déconnexion se teste application par application : back-channel quand c’est possible, sessions locales courtes sinon.
- Le reverse proxy raccorde ce qui ne parle aucun protocole, à condition que l’application ne soit joignable que par lui.
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é | Verdict | Précision |
|---|---|---|
| OIDC code d'autorisation avec PKCE, client credentials | Natif | PKCE imposable par client (PKCE Method) ou pour tout un realm avec l'executor pkce-enforcer d'une client policy. |
| Authentification du client par secret, JWT signé ou certificat X.509 | Natif | Quatre authenticators : Client Id and Secret, Signed Jwt, Signed Jwt with Client Secret, X509 Certificate. Le mTLS demande que le certificat client parvienne jusqu'à Keycloak. |
| SAML 2.0 (métadonnées, signature, chiffrement, NameID, mappers) | Natif | Keycloak joue le rôle d'IdP SAML ; import des métadonnées du SP depuis la console. |
| Déconnexion OIDC front-channel et back-channel, SLO SAML | Natif | Côté Keycloak, tout est disponible ; ce qui fonctionne réellement dépend de ce que l'application implémente. |
| Applications sans OIDC ni SAML via reverse proxy | Natif | Rien à développer dans Keycloak : un client OIDC standard. Le travail est côté proxy (mod_auth_openidc, oauth2-proxy) et côté réseau. |
| Protocoles propriétaires (Kerberos applicatif, CAS, en-têtes signés spécifiques) | Extension | Kerberos/SPNEGO existe pour l'authentification de l'utilisateur, mais un protocole applicatif non standard demande une extension ou un composant tiers. |
Vérifier ce que vous avez retenu
5 questions
-
Une application monopage JavaScript sans backend doit se raccorder. Quelle configuration retenir ?
Un navigateur ne garde pas de secret : le client est public, et PKCE protège l'échange du code. L'implicit et le grant password sont déconseillés par RFC 9700.
-
Un traitement batch appelle une API de nuit, sans utilisateur. L'hébergeur sait gérer une paire de clés mais pas une PKI de certificats clients. Quelle authentification du client recommander ?
La clé privée ne quitte pas le client et la rotation passe par le JWKS. Le mTLS demande une PKI et un chemin TLS de bout en bout jusqu'à Keycloak ; le secret partagé reste acceptable mais plus faible.
-
Un éditeur affirme gérer « la déconnexion unique ». Quelle question permet de savoir si un logout dans une autre application le déconnectera vraiment ?
Appeler end_session ne concerne que la déconnexion initiée par l'application elle-même. Pour être déconnectée quand une autre application l'est, elle doit recevoir et traiter la notification de Keycloak.
-
Une application ancienne ne sait lire que l'en-tête HTTP REMOTE_USER. Vous proposez mod_auth_openidc devant elle. Quel risque devez-vous traiter en priorité ?
L'application fait confiance à un en-tête. Si elle est joignable sans passer par le proxy, n'importe qui peut l'envoyer. Le cloisonnement réseau est la condition de sécurité de ce modèle.
-
Un fournisseur de services SAML demande un NameID stable et opaque, sans donnée personnelle. Quel format choisir ?
persistent fournit un identifiant opaque, stable et propre au SP. transient change à chaque session, email et username exposent une donnée personnelle et peuvent changer.