OAuth 2.0 et OpenID Connect

Comprendre les rôles et les flux d'OAuth 2.0, puis ce qu'OpenID Connect ajoute pour authentifier l'utilisateur (ID token, claims, discovery, JWKS).

Fondations Cours 2 sur 17 de Keycloak Academy 35 min 4 questions de quiz Cours suivant : SAML 2.0

Sur cette page
  1. Les quatre rôles d’OAuth 2.0
  2. Clients publics et confidentiels
  3. Le flux authorization code avec PKCE
  4. Le flux client credentials
  5. Les scopes
  6. Les flux à proscrire
  7. OpenID Connect : la couche d’identité
  8. Anatomie d’un ID token
  9. Discovery et JWKS
  10. Quiz

OAuth 2.0 et OpenID Connect (OIDC) sont les deux protocoles que vous croiserez le plus souvent avec Keycloak. OAuth 2.0 permet à une application d’obtenir un droit d’accès à une API. OIDC ajoute une couche d’identité : il dit à l’application qui est l’utilisateur. Ce cours présente les deux, dans cet ordre.

Les quatre rôles d’OAuth 2.0

La RFC 6749 définit quatre rôles.

RôleCe qu’il faitExemple
Resource ownerPossède les données, accorde l’accèsL’utilisateur
ClientApplication qui veut accéder aux donnéesLe portail RH
Authorization serverAuthentifie et délivre les jetonsKeycloak
Resource serverAPI qui protège les donnéesL’API de gestion des congés

Le client ne reçoit jamais le mot de passe de l’utilisateur. Il reçoit un access token, un jeton à durée de vie courte qu’il présente à l’API dans l’en-tête Authorization: Bearer .... Il peut aussi recevoir un refresh token, plus durable, qui sert uniquement à demander un nouvel access token au serveur d’autorisation sans solliciter l’utilisateur.

Clients publics et confidentiels

OAuth 2.0 distingue deux types de clients selon leur capacité à garder un secret.

  • Un client confidentiel s’exécute sur un serveur. Il détient un secret (ou une clé privée) et s’authentifie auprès du serveur d’autorisation. Exemple : une application Java côté serveur.
  • Un client public s’exécute chez l’utilisateur : application JavaScript dans le navigateur, application mobile. Tout ce qu’il contient peut être lu. Il n’a donc pas de secret.

Ce choix conditionne les flux possibles et les protections à mettre en place.

Le flux authorization code avec PKCE

C’est le flux de référence pour connecter un utilisateur, que le client soit public ou confidentiel. PKCE (Proof Key for Code Exchange, RFC 7636) l’a renforcé.

Flux authorization code avec PKCE Navigateur Client application Serveur d'autorisation Clic « Se connecter » 1 Génère code_verifiercode_challenge = SHA256(…) Redirection vers /auth avec code_challenge 2 L'utilisateur s'authentifie 3 Redirection vers redirect_uri ?code=…&state=… 4 POST /token code + code_verifier 5 Access token, ID token, refresh token 6

La requête de l’étape 2 ressemble à ceci :

GET /realms/demo/protocol/openid-connect/auth?response_type=code
    &client_id=portail-rh
    &redirect_uri=https%3A%2F%2Fportail.exemple.fr%2Fcallback
    &scope=openid%20profile%20email
    &state=af0ifjsldkj
    &code_challenge=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM
    &code_challenge_method=S256 HTTP/1.1
Host: idp.exemple.fr

Puis l’échange du code à l’étape 5, fait par le client hors du navigateur :

POST /realms/demo/protocol/openid-connect/token HTTP/1.1
Host: idp.exemple.fr
Content-Type: application/x-www-form-urlencoded

grant_type=authorization_code
&code=SplxlOBeZQQYbYS6WxSbIA
&redirect_uri=https%3A%2F%2Fportail.exemple.fr%2Fcallback
&client_id=portail-rh
&code_verifier=dBjftJeZ4CVP-mJ92K9dTZ6aMyvBhdV1dTkqEC9pV7c

Le paramètre state protège contre la falsification de requête (CSRF). Le code_verifier prouve que celui qui échange le code est bien celui qui a lancé le flux. Un client confidentiel ajoute en plus son authentification (secret ou assertion signée).

Le flux client credentials

Quand aucun utilisateur n’est en jeu, un programme demande un jeton en son propre nom. C’est le cas d’un traitement de nuit qui appelle une API. Seul un client confidentiel peut l’utiliser.

POST /realms/demo/protocol/openid-connect/token HTTP/1.1
Host: idp.exemple.fr
Content-Type: application/x-www-form-urlencoded
Authorization: Basic YmF0Y2gtcGFpZTpzZWNyZXQ=

grant_type=client_credentials&scope=paie.lecture

Aucun ID token n’est émis : il n’y a pas d’utilisateur à identifier.

Les scopes

Un scope est une permission demandée par le client, sous forme d’une chaîne de caractères : paie.lecture, profile, email. Le serveur d’autorisation peut accorder tout ou partie des scopes demandés. L’API vérifie ensuite que le jeton présenté contient le scope nécessaire. Les scopes expriment ce que le client a le droit de faire au nom de l’utilisateur, pas l’ensemble des droits de l’utilisateur.

Les flux à proscrire

La RFC 9700 (janvier 2025) rassemble les bonnes pratiques de sécurité OAuth 2.0. Deux flux de la RFC 6749 y sont écartés.

FluxProblèmePosition de la RFC 9700
ImplicitL’access token transite dans l’URL du navigateur : historique, journaux, fuite par le RefererNe devrait pas être utilisé (SHOULD NOT) ; utiliser authorization code
Resource owner password credentialsLe client voit le mot de passe ; incompatible avec le MFA et la fédérationNe doit pas être utilisé (MUST NOT)

La RFC 9700 recommande aussi PKCE pour tous les clients, la comparaison exacte des redirect_uri enregistrées et la limitation des droits des access tokens.

OpenID Connect : la couche d’identité

OAuth 2.0 ne dit rien de l’utilisateur. OIDC ajoute :

  • le scope openid, qui déclenche le comportement OIDC ;
  • l’ID token, un JWT destiné au client qui décrit l’authentification ;
  • des claims standard (sub, name, email, etc.) et des scopes pour les demander (profile, email, address, phone) ;
  • l’endpoint userinfo, qui renvoie les claims de l’utilisateur sur présentation d’un access token ;
  • la discovery, un document JSON qui décrit le serveur.

Anatomie d’un ID token

Un JWT est composé de trois parties encodées en Base64URL et séparées par des points : en-tête, contenu, signature. Voici un ID token décodé.

{
  "alg": "RS256",
  "typ": "JWT",
  "kid": "x7Fq2kVn9pLr3sT8uW1yZ4aB6cD0eG5h"
}
{
  "iss": "https://idp.exemple.fr/realms/demo",
  "sub": "8f3c2a10-5d4e-4b7a-9c1f-2e6d7a8b9c0d",
  "aud": "portail-rh",
  "exp": 1762354800,
  "iat": 1762354500,
  "auth_time": 1762354490,
  "nonce": "n-0S6_WzA2Mj",
  "acr": "1",
  "azp": "portail-rh",
  "preferred_username": "cmartin",
  "name": "Claire Martin",
  "email": "claire.martin@exemple.fr",
  "email_verified": true
}

Le client doit vérifier la signature avec la clé publique désignée par kid, puis que iss est l’émetteur attendu, que aud contient son propre client_id, que exp n’est pas dépassé et que nonce correspond à la valeur envoyée dans la requête d’autorisation. auth_time et acr indiquent quand et avec quel niveau l’utilisateur s’est authentifié ; le cours sur le MFA y revient.

Discovery et JWKS

Chaque serveur OIDC publie un document de configuration à une adresse fixe : l’émetteur suivi de /.well-known/openid-configuration (OpenID Connect Discovery 1.0). On y trouve les adresses des endpoints, les flux et algorithmes supportés, et jwks_uri.

Le JWKS (JSON Web Key Set) est la liste des clés publiques de signature du serveur :

{
  "keys": [
    {
      "kid": "x7Fq2kVn9pLr3sT8uW1yZ4aB6cD0eG5h",
      "kty": "RSA",
      "alg": "RS256",
      "use": "sig",
      "n": "0vx7agoebGcQSuu...",
      "e": "AQAB"
    }
  ]
}

Grâce à la discovery et au JWKS, une bibliothèque cliente se configure avec la seule adresse de l’émetteur. Lors d’une rotation de clés, le serveur publie la nouvelle clé dans le JWKS et les clients la récupèrent sans intervention.

Lire le document de discovery de Keycloak

10 min

Installer le lab
  1. Démarrez l’environnement du dossier keycloak-academy-lab/ et vérifiez que Keycloak répond sur http://localhost:8080.
  2. Récupérez le document de discovery du realm master et affichez quelques champs.
curl -s http://localhost:8080/realms/master/.well-known/openid-configuration \
  | jq '{issuer, authorization_endpoint, token_endpoint, userinfo_endpoint, jwks_uri}'
  1. Vérifiez que issuer vaut http://localhost:8080/realms/master et que les autres adresses commencent par cet émetteur.
  2. Repérez les flux annoncés dans grant_types_supported et les méthodes PKCE dans code_challenge_methods_supported.
curl -s http://localhost:8080/realms/master/.well-known/openid-configuration \
  | jq '.grant_types_supported, .code_challenge_methods_supported'
  1. Ouvrez l’adresse jwks_uri et notez le kid et l’alg de la clé dont use vaut sig.

Si jq n’est pas installé, ouvrez simplement l’adresse dans votre navigateur : les navigateurs récents affichent le JSON de façon lisible.

Vérifier ce que vous avez retenu

4 questions

  1. Une application monopage (SPA) en JavaScript doit connecter des utilisateurs. Quel flux faut-il retenir selon les bonnes pratiques actuelles ?

  2. Quel jeton une application doit-elle valider pour savoir qui vient de se connecter ?

  3. Que protège PKCE dans le flux authorization code ?

  4. Comment une application obtient-elle la clé publique qui permet de vérifier la signature d'un JWT émis par le serveur ?