Publié le 14/02/2018 Par Matthieu MABYRE

OpenID Connect (OIDC) spécifie une interface HTTP Restful d’authentification et se base sur le protocole OAuth2 pour faire de la délégation d’autorisation, c’est à dire que dans la grande majorité des cas, l’utilisateur final n’aura plus besoin de fournir directement ses informations d’identification à une application tierce. OIDC utilise également le formalisme d’échange JWT (JSON Web Token) pour transmettre l’identité des utilisateurs aux applications, ainsi que leurs rôles/habilitations. Dans cet article nous nous intéresserons à déterminer comment est gérée et sécurisée cette relation de confiance entre les utilisateurs et les applications, tout en garantissant l’intégrité des données.

I. Description du protocole OAuth2

OAuth2 spécifie un protocole de délégation d’accès (https://tools.ietf.org/html/rfc6749). Son but principal est donc de décrire comment l’accès à des API sécurisées d’une application ou d’un site web (fournisseur) va être délégué à une autre application (consommateur).

Le protocole distingue 4 rôles principaux :

  • Resource Owner : celui qui détient les ressources,
  • Resource Server : serveur qui héberge les ressources protégées,
  • Client : application cliente (front, back ou mobile) qui demande l’accès aux ressources,
  • Authorization Server : serveur qui génère des jetons (tokens) pour le client et qui seront transmis lors des requêtes vers le serveur de ressources.

A. La notion de token :

  • Access Token : token permettant de valider un accès à un service sécurisé (une autorisation) avec une durée de vie définie. Il est indispensable.
  • Refresh Token : token dit « de renouvellement » de longue durée permettant de demander la création d’un nouvel Access Token si ce dernier est expiré. Il est émis au même moment que l’Access Token mais n’est pas envoyé à chaque requête. Le Refresh Token doit être stocké par l’application cliente de manière sécurisée.

B. Enregistrement des clients :

Il faut noter que dans le cadre du protocole OAuth2, chaque application cliente qui désire accéder à des ressources protégées doit au préalable s’enregistrer auprès du serveur d’autorisation (généralement via un formulaire). La spécification OAuth2 précise les paramètres standards que les clients doivent renseigner lors du processus d’enregistrement:

  • Application Name : nom de l’application,
  • Redirect URI (ou Callback URL) : URI (ou URL) de l’application cliente vers laquelle seront faites les redirections par le serveur d’autorisation, une fois l’accès aux ressources autorisé (ou bien lorsque l’accès sera refusé),
  • Grant Type(s) : types d’autorisation qui pourront être utilisés par le client lors de la demande de ressources protégées.

Le serveur d’autorisation délivre en retour un couple client_id/client_secret :

  • client_id : chaîne de caractères générée de façon aléatoire qui identifie une application cliente de manière unique,
  • client_secret : chaîne de caractères représentant la clé secrète du client et qui sera utilisée lors de l’appel à certaines API nécessitant une entête HTTP Authorization.

Le flux de demande d’accès à des ressources sécurisées peut donc être représenté de la manière générique suivante:

 

 

  1. L’application cliente envoie une demande d’accès aux ressources protégées de l’utilisateur en précisant notamment son identité (client_id), le type d’autorisation et les scopes souhaités. Les scopes sont déterminés par le serveur d’autorisation au préalable. Plus l’API est découpée en scopes petits, et plus le profil de chaque client est précis (et donc limité). Chaque application cliente ne connaît que les scopes qu’elle peut utiliser.
  2. Si l’utilisateur approuve la requête, un droit d’accès est renvoyé au client.
  3. Le client demande alors un Access Token au serveur de jetons en fournissant son identité (ex: des credentials client), ainsi que son droit d’accès reçu précédemment.
  4. Si l’identité est validée (le client est bien authentifié) et que le droit d’accès est valide, le serveur de jetons lui délivre un Access Token.
  5. Le client peut ensuite demander l’accès aux ressources protégées au serveur de ressources en présentant son Access Token.
  6. Si l’Access Token fourni est valide, les ressources demandées sont renvoyées au client.

Notons que la spécification OAuth2 impose que tous ces échanges aient lieu en HTTPS.

Selon le type d’autorisation indiqué par le client, ce schéma sera implémenté de différentes façons. Dans la spécification OAuth2, on distingue 4 types d’autorisations:

  • Authorization Code : utilisé si l’application cliente est située côté serveur. Cas le plus implémenté.
  • Implicit : utilisé si l’application cliente est située côté client (ex: une application Javascript ou une application mobile) et qu’aucun autre type d’autorisation n’est utilisable. Ce mode est moins sécurisé car le token ne reste pas côté serveur, mais est exposé côté client et peut être intercepté.
  • Resource Owner Credentials : les identifiants de connexion sont envoyés au client puis au serveur d’autorisation. Cela implique qu’il y ait une confiance absolue entre les deux. Souvent utilisé lorsque le client a été développé par la même entité que celle fournissant le serveur d’autorisation (ex: un accès à des ressources sécurisées d’un sous-domaine), ce type d’autorisation est très fortement déconseillé car OAuth a été pensé justement pour que les identifiants de connexion ne soient plus transmis aux applications tierces. De plus il n’y a pas de vérification de l’URL de callback.
  • Client Credentials : utilisé lorsque le client est lui-même le propriétaire des données. Il n’y a donc pas d’autorisation spécifique à obtenir de la part de l’utilisateur. Les échanges commencent directement à l’étape 3.

Il est important de retenir qu’OAuth2 ne gère ni l’authentification ni même l’autorisation. A aucun moment il n’est question d’informations utilisateur, de rôles ou d’habilitations. Afin d’avoir une solution d’identité complète, il est nécessaire d’utiliser le protocole OpenID Connect.

 

II. Description du protocole OpenID Connect

OpenID Connect est un protocole qui gagne en popularité car c’est une surcouche à OAuth2 (il est capable de répondre à tous ses cas d’utilisation), et ajoute de nouvelles fonctionnalités qui manquaient à OAuth2 :

  • La prise en charge de l’authentification,
  • La notion d’ID Token,
  • La gestion de la session SSO (ex: le Single Logout),
  • Une nouvelle API pour récupérer les informations utilisateur (User Info endpoint),
  • Standardisation des informations utilisateurs,
  • Un système de découverte du serveur OpenID afin de permettre aux clients de s’enregistrer par eux-mêmes.

A. La notion d’ID Token :

Un ID Token est un jeton auto-portant qui contient l’identité d’un utilisateur. Il est véhiculé au format JWT (nous rentrerons dans les détails en partie 3), et est constitué principalement :

  • des paramètres de l’authentification :
    • date d’expiration,
    • date de création,
    • date d’authentification,
    • des moyens de contrôle permettant de valider l’ID Token et l’Access Token.
  • des accréditations (rôles, habilitations) de l’utilisateur :
    • formalisme à déterminer par le fournisseur d’identité.
  • des attributs (claims) de l’utilisateur.

Les attributs sont associés à des « scopes » :

  • attributs standards :
    • scope profile : nom, prénom, surnom, date de naissance, …
    • scope email : email, email vérifié
    • scope address : adresse
    • scope phone : numéro de téléphone, numéro de téléphone vérifié
  • attributs privés : attributs proposés par le fournisseur d’identité. Il est nécessaire de les spécifier afin d’éviter toute collision avec des claims existants.

 

Exemple d’ID Token :

{
"iss": "http://server.meritis.fr",
"sub": "24400320",
"aud": "s6BhdRkqt3",
"nonce": "n-0S6_WzA2Mj",
"exp": 1515604697,
"iat": 1515593897,
"name": "Matthieu Mabyre",
"given_name": "Matthieu",
"family_name": "Mabyre",
"gender": "male",
"email": "matthieu.mabyre@meritis.fr",
"acr": ["role1","role2", "role3"]
}

La signification des différents champs peut être retrouvée sur la documentation officielle (http://openid.net/specs/openid-connect-core-1_0.html#IDToken).

OpenID Connect propose plusieurs interfaces (endpoints) :

  • authorization : pour authentifier un utilisateur,
  • token : pour demander un token (access / refresh / ID),
  • user info : pour récupérer des informations sur l’utilisateur (son identité, ses droits),
  • revocation : pour supprimer un token (access / refresh),
  • introspection: pour valider un token (access / refresh).

D’autres interfaces optionnelles sont disponibles pour l’enregistrement de clients, la découverte de fournisseurs OpenID Connect, etc.

B. La notion d’Authorization Flow

OpenID Connect propose trois algorithmes pour déterminer comment retourner les tokens :

Authorization Code Flow :

L’algorithme retourne un code d’autorisation pour ensuite récupérer des tokens :

  • les tokens sont retournés uniquement par l’interface token,
  • la récupération d’un token d’accès s’effectue en deux étapes :
    • un code est retourné par l’interface authorization,
    • ce code est envoyé par le client à l’interface token.
  • le client doit être enregistré auprès du fournisseur OpenID (via un identifiant et un secret),
  • s’applique très bien aux applications mobiles, web et back-end,
  • algorithme le plus implémenté.

Implicit Flow :

L’algorithme retourne directement les tokens.

  • les tokens sont retournés directement par l’interface authorization (l’interface token n’est plus utilisée),
  • il n’y a pas d’enregistrement des clients,
  • il n’y a pas de notion de Refresh Token,
  • les tokens à durée de vie longue ne sont pas autorisés,
  • algorithme pour les applications type Javascript (sans back-end).

Hybrid Flow :

C’est un mix entre l’Authorization Code Flow et l’Implicit Flow.

  • le séquencement est identique à l’Authorization Code Flow à l’exception du fait que l’interface authorization peut retourner le code, l’ID Token et l’Access Token,
  • le Refresh Token quant à lui s’obtient par un appel à l’interface token,
  • algorithme très peu utilisé.

 

Focus sur l’algorithme d’authentification Authorization Code Flow:

Le diagramme ci-dessous présente l’algorithme d’authentification dans sa forme basique.

Les points suivants sont à noter :

  • La spécification prévoit que l’OpenID Provider fournisse (ou délègue à un autre OpenID Provider) la mire d’authentification,
  • La spécification prévoit également que l’OpenID Provider demande le consentement de l’utilisateur sur l’accès aux données de son identité (accès à des périmètres précis, mail, adresse, téléphone, …).

Notons que la récupération des informations utilisateurs « UserInfo request » n’est pas obligatoire puisque l’ID Token peut être alimenté avec les informations utilisateurs lors de la demande de token.

III. Focus sur JWT

JWT (JSON Web Token) est un format d’échange d’informations standard et sécurisé (https://tools.ietf.org/html/rfc7519).

Un token JWT est décomposé en trois parties, séparées par le caractère . :

header 

Le header (JOSE header) décrit l’algorithme utilisé pour signer ou chiffrer le token. Par exemple pour une signature utilisant l’algorithme HS256 (HMAC with SHA-256), nous aurions : {"alg":"HS256","typ":"JWT"}

Le header doit ensuite être encodé en base64.

payload

C’est le contenu du token. Par exemple notre ID Token décrit précédemment :

{
"jti": "f232b54cb285452db02770c9d16f8f212151",
"iss": "http://server.meritis.fr",
"sub": "24400320",
"aud": "s6BhdRkqt3",
"nonce": "n-0S6_WzA2Mj",
"exp": 1515604697,
"iat": 1515593897,
"name": "Matthieu Mabyre",
"given_name": "Matthieu",
"family_name": "Mabyre",
"gender": "male",
"email": "matthieu.mabyre@meritis.fr",
"acr": ["role1","role2", "role3"]
}

 

Le champ jti (JWT id) correspond à l’identifiant du token. Par exemple un UUID généré aléatoirement.

Le payload doit également être encodé en base64 par la suite.

signature du jeton 

Elle est effectuée en utilisant l’algorithme de signature (défini dans le header) à partir :

  • du header au format base64 url encodé,
  • du payload au format base64 url encodé,
  • de la clé privée du serveur d’autorisation.

La signature des tokens vise à garantir que les tokens auto-portants générés par le serveur d’autorisation ont bien été générés par ce serveur, et qu’ils n’ont pas été altérés par un tiers.

Exemple:

HMACSHA256(
Base64UrlEncode(header) + "." +
Base64UrlEncode(payload),
clé privé
)

 

Au final, nous obtenons un jeton JWS (S pour Signed) encodé en base64 (sa taille va dépendre de la taille du payload) :

 

Notons que si nous avions décidé de chiffrer le jeton JWT, ce serait devenu un JWE (E pour Encrypted).

Le site https://jwt.io/ est utile pour encoder/décoder des flux JWT.

IV. Quelques exemples de requêtes/réponses

A. Authentification

Authentication Request

GET /authorization?
response_type=code
&scope=openid%20profile%20email
&client_id=s6BhdRkqt3
&state=af0ifjsldkj
&redirect_uri=https%3A%2F%2Fclient.example.org%2Fcb
HTTP/1.1
Host: server.example.com

 

Ici l’application cliente effectue une demande de ressource protégée en indiquant au serveur d’autorisation qu’elle souhaite utiliser l’algorithme Authorization Code Flow (response_type=code), et en fournissant son identifiant (client_id), le scope souhaité, ainsi que l’URI de redirection (redirect_uri). Le paramètre state ajoute un niveau de sécurité supplémentaire afin de se prémunir, par exemple, des attaques de type CSRF. Il est vu comme un identifiant de session.

Authentication Response

HTTP/1.1 302 Found
Location: 
code=SplxlOBeZQQYbYS6WxSbIA
&state=af0ifjsldkj

 

La réponse s’effectue via une redirection vers l’application cliente en indiquant l’Authorization Code. L’état (state) est bien sûr conservé dans l’échange.

B. Demande de jetons

Token Request

POST /token HTTP/1.1
Host: server.example.com
Content-Type: application/x-www-form-urlencoded
Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW
grant_type=authorization_code
&code=SplxlOBeZQQYbYS6WxSbIA
&redirect_uri=https%3A%2F%2Fclient.example.org%2Fcb

 

La demande de token est donc effectuée en passant l’authorization code (code=SplxlOBeZQQYbYS6WxSbIA), l’URI de redirection (redirect_uri) et bien sûr il faut préciser au serveur d’autorisation que le type d’autorisation fournie est un code (grant_type=authorization_code). Au niveau des entêtes HTTP, l’API POST /token impose de positionner les entêtes suivantes :

  • Content-Type : le type de contenu,
  • Authorization : les credentials d’authentification. Ici, il s’agit d’une authentification basique (Basic) où la valeur correspond à la chaîne client_id:client_secret encodée en base64.

Token Response

HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
Cache-Control: no-store
Pragma:no-cache
{
"access_token" : "2YotnFZFEjr1zCsicMWpAA",
"token_type" : "bearer",
"expires_in" : 3600,
"scope" : "openid profile email",
"id_token" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}

Les données sont renvoyées sous forme d’un flux JSON contenant un jeton d’accès (access_token) ainsi que son type (token_type), sa durée de vie avant expiration (expires_in) et son scope. Un jeton d’identité en JWT a également été généré (id_token).

 

C. Demande des informations utilisateurs

User Info Request

GET /userinfo HTTP/1.1
Host: server.example.com
Authorization: Bearer 2YotnFZFEjr1zCsicMWpAA

L’Access Token est passé au serveur d’autorisation via l’entête HTTP Authorization et en précisant son type (Bearer) devant.

User Info Response

HTTP/1.1 200 OK
Content-Type: application/json
{
"iss": "http://server.meritis.fr",
"sub": "24400320",
"aud": "s6BhdRkqt3",
"nonce": "n-0S6_WzA2Mj",
"exp": 1515604697,
"iat": 1515593897,
"name": "Matthieu Mabyre",
"given_name": "Matthieu",
"family_name": "Mabyre",
"gender": "male",
"email": matthieu.mabyre@meritis.fr,
"acr": ["role1","role2", "role3"]
}

Conclusion

OpenID Connect et OAuth 2 apportent une réelle valeur ajoutée aux organisations souhaitant disposer d’une fédération d’identités centralisée. Ils sont devenus les standards incontournables lorsqu’il s’agit d’adresser des problématiques SSO entre services et applications de leur SI. En effet les informations étant normalisées, tout comme leur formalisme d’échange (via JWT), l’interopérabilité entre les applications est garantie, et l’intégration n’en est que plus facile et rapide.

Un exemple concret est la plateforme d’authentification centrale FranceConnect de l’état français qui permet, par exemple, de se connecter avec son identifiant des impôts pour connaître le solde de points de son permis de conduire.

Vos commentaires

  1. Par Nicolas, le 16 Fév 2018

    Il reste plus qu’à attaquer l’article sur le SAML et on aura fait le tour de la question 😉

  2. Par Stéphane, le 05 Avr 2018

    Très bel article, très clair.

    La notion de délégation d’authentification d’un SP auprès d’un IdP est facile à comprendre et mène naturellement au SSO.
    Par contre le mot fédération est utilisé pour désigner plusieurs choses et ne me semble pas clair :
    – certains fournisseurs d’identité désigne par fédération la capacité à s’interfacer avec plusieurs sources d’identités (LDAP,AD,BDD)
    – dans d’autres contexte, le terme de fédération désigne une action explicite d’association de compte issus de 2 systèmes distincts par un utilisateur
    – enfin, la notion de fédération désigne également la capacité d’un système à associer de façon dynamique (via un attribut pivot comme le mail) ou statique (par stockage d’une association d’identifiants) le lien entre 2 identités issues de 2 systèmes distincts
    Pourriez-vous m’éclairer à ce sujet ?

  3. Par Matthieu MABYRE, le 06 Avr 2018

    Bonjour, merci pour vos retours.

    Ici le terme de fédération doit être vu comme une centralisation des données d’identité, qui oui par exemple peuvent se trouver dans diverses sources telles un annuaire LDAP, un AD, ou autre…, afin que l’utilisateur n’ai à s’authentifier qu’une seule fois par session. Ainsi il pourra naviguer et appeler des services sécurisés d’autres fournisseurs de manière fluide, sans avoir à se ré authentifier.

  4. Par Ghafir ZEGGAF TAHRI, le 26 Juin 2018

    Bonjour ,
    Merci d’abord pour cet article , j’ai une question :
    si le token est hacké (par un hacker sur le réseau ) donc il peux accéder au service tant que le token n’a pas espérée ??

    Cdt

  5. Par Dominique, le 15 Nov 2018

    Bonjour,
    Oui merci pour cet article très bien réalisé.
    Comme Nicolas je suis preneur du même type d’article sur le SAML V2 et son mode IdP Inititated, non présent dans OIDC/OAuth2 😉

  6. Par Otto, le 20 Août 2019

    Superbe article, très clair et concis.

  7. Par Quentin, le 06 Déc 2019

    Super article, merci !

Publier un commentaire

Auteur

Matthieu MABYRE

Architecte Logiciel/Expert Java - 11 ans d’expérience dans la réalisation d’applications nécessitant conception, études d’impacts, mise en place d’architectures logicielles, implémentation et pilotage technique de projets.

Découvrir ses autres articles

Pas d'autres articles publiés par cet auteur pour le moment