i-TegoDocumentationAuthentification > OpenID Connect OAuth Serveur dédié > Développer > OpenID Connect > API OpenID Connect : Points d’extrémité

API OpenID Connect : Points d’extrémité

  publié le par i-Tego WM

Les points d’extrémité d’OpenID Connect jouent un rôle semblable à ceux du protocole OAuth 2.0 mais sont plus complets.

Points d’extrémité définis par OpenID Connect

Le point d’extrémité d’autorisation et le point d’extrémité de jeton sont tous deux situés sur le serveur d’autorisation (ce serveur).

Le point d’extrémité de redirection est situé dans l’application cliente. Il revient donc à l’administrateur de cette application de développer le code du point d’extrémité de redirection.

La spécification d’OpenID Connect ne décrit pas comment l’URI de ces points d’extrémité est définie. C’est à chaque développeur de décider. Cependant, l’observation de différents serveurs permet de dégager des options courantes, que nous avons adoptées.

Notons que OpenID Connect prévoit la découverte des points d’extrémités, ce que OAuthSD implémente à cette URL : [http://oa.dnc.global/.well-known/openid-configuration]

Voici comment OAuthSD définit les points d’extrémité du protocole OpenID Connect :

Point d’extrémité d’autorisation (Authorization Endpoint)
https://oa.dnc.global/authorize
Usage dans le cadre du flux Autorisation via un code : OpenID Connect : Obtenir une autorisation pour l’application cliente

Le point d’extrémité d’autorisation est le point d’extrémité sur le serveur d’autorisation auquel l’agent de l’utilisateur final (user-agent : le navigateur ou l’application mobile etc.) est redirigé par l’application cliente, pour permettre à l’utilisateur final de s’authentifier et d’accorder les autorisations demandées par l’application cliente.

Lors du traitement de la demande d’authentification, le serveur crée un jeton d’accès et un jeton d’identification. Ces jetons seront associés à l’ID de l’utilisateur final, ce qui ouvre une notion de session.

Dans le cas du flux d’autorisation via un code (celui qui nous intéresse le plus), à ce stade, il n’est retourné qu’un code d’autorisation de durée de vie très courte [1]. Les jetons ne seront retournés qu’en réponse à l’appel du point d’extrémité token, dans une liaison serveur-serveur, ce qui fonde la sécurité de ce flux.

Dans le cas des flux implicites et hybrides, utilisés pour des applications sans serveur (back-end), les jetons sont directement retournés dans la réponse et sont donc visibles par l’agent de l’utilisateur, ce qui est considéré comme une faiblesse de ces flux.

Point d’extrémité de redirection, ou URI de retour à l’application cliente (Redirection Endpoint)
Le point d’extrémité de redirection est le point d’extrémité dans l’application cliente vers lequel l’agent de l’utilisateur est redirigé, après avoir obtenu l’autorisation au point d’extrémité d’autorisation. Cet URI est défini par l’administrateur d’une application cliente quand il l’inscrit sur ce serveur. Voir : OpenID Connect : Lier une application cliente au serveur OAuthSD. Ce point d’extrémité pourrait être distinct de celui du protocole OAuth 2.0 car OpenID Connect prévoit des échanges différents et requiert que le jeton obtenu soit vérifié par l’application cliente. Cet article explique comment faire : OpenID Connect : Exemples complets du flux d’Autorisation via un code puis requête UserInfo

Point d’extrémité de jeton (Token Endpoint)
https://oa.dnc.global/token
Usage dans le cadre du flux Autorisation via un code : OpenID Connect : Obtenir les jetons d’accès

Le point d’extrémité de jeton est le point d’extrémité sur le serveur d’autorisation auquel s’adresse l’application cliente, avec le code d’autorisation et l’ID client, pour obtenir un jeton d’accès et un jeton d’identité ou rafraîchir des jetons ayant expiré.

Le jeton d’accès est utilisé pour effectuer des appels authentifiés vers une API sécurisée, tandis que le jeton d’identification contient des attributs de profil utilisateur représentés sous la forme de déclarations (claims). Les deux JWT ont une date d’expiration indiquée par la revendication exp (parmi d’autres mesures de sécurité, comme la signature).

Point d’extrémité d’introspection (Introspection Endpoint)
https://oa.dnc.global/introspect
Usage : Demande d’introspection.

La "norme" OpenID Connect ne définit pas l’introspection. En effet, dans une approche simpliste, OpenID Connect ne protège que les données UserInfo qui, étant situées sur le même serveur d’authentification, ne nécessitent pas l’introspection.
Mais si on souhaite utiliser OpenID Connect pour accéder à des données protégées situées sur un serveur de ressource distinct du serveur d’authentification, l’introspection est une des deux méthodes applicables pour permettre à ce serveur de valider le jeton JWT. La problématique générale est exposée ici : Validation du jeton par une ressource protégée.

Notons que l’introspection n’est pas une particularité d’OAuthSD, d’autres implémentations offrant également cette fonctionnalité.

Point d’extrémité d’informations sur l’utilisateur final (UserInfo)
https://oa.dnc.global/userinfo
Pour réaliser l’authentification, le protocole OpenID Connect permet à une application cliente d’accéder à l’identité de l’utilisateur final ayant accordé l’autorisation, ainsi qu’à différentes informations selon le scope passé dans la requête.
Usage :
- Demande d’informations sur l’utilisateur (UserInfo Endpoint),
- Réponse UserInfo.
Références :
- UserInfo Endpoint

Point d’extrémité d’informations sur les clefs (Keys Endpoint)
Ce point permet à un serveur de ressource d’accéder à la clé publique qui lui permettra de valider le jeton d’identité, comme exposé dans cet article : Validation du jeton par une ressource protégée.

https://oa.dnc.global/keys

Ce sujet est traité plus en détail ici : API OpenId Connect : Point d’extrémité d’informations sur les clefs (Keys Endpoint)

Point d’extrémité de déconnexion (Logout Endpoint)

https://oa.dnc.global/logout.php

Ce point permet la déconnexion de l’utilisateur au niveau du serveur de toutes ses sessions sécurisées. Si un cookie SLI avait été généré, il est détruit, interdisant ainsi le rafraîchissement automatique des sessions de l’utilisateur ouvertes à partir d’autres applications.
L’appel de Logout se fait exactement comme Introspect, avec un jeton d’identité valide.

Découverte des points d’extrémité
Conformément à la spécification OpenID Connect Discovery 1.0, OAuthSD expose ses métadonnées à l’URI :

https://oa.dnc.global/.well-known/openid-configuration

Ce sujet est traité plus en détail ici : API OpenID Connect : Découverte.

Notes

[1Nous prévoyons de le rendre à usage unique. Il ne semble pas que la norme le prévoit, est-ce convenable ?

API OpenID Connect : Point d’extrémité d’autorisation (Authorization Endpoint)

  publié le par i-Tego WM

C’est le point d’extrémité sur le serveur d’autorisation auquel l’user-agent (en général un navigateur Web) de l’utilisateur final est redirigé, pour lui permettre de s’identifier et d’accorder des autorisations à l’application cliente.
Dans le cas du flux d’autorisation avec code (Authorization code flow), le contrôleur Authorize redirige l’user-agent sur le point d’extrémité Token avec un code d’autorisation.
Le flux implicite permet d’obtenir directement les jetons.

Point d’extrémité d’autorisation (Authorization Endpoint)

https://oa.dnc.global/authorize

Forme de la demande d’autorisation

Exemples d’appel Authorize :

PHP

  1.     $data = array(
  2.         'response_type' => 'code',
  3.         'client_id' => 'chemin_openid',
  4.         'state' =>  $oauth_state,
  5.         'scope' => 'openid profile',
  6.     );
  7.  
  8. $authorization_endpoint = 'https://oa.dnc.global/authorization';
  9.  
  10. $authorization_endpoint .= '?' . http_build_query($data);
  11.     header('Location: ' . $authorization_endpoint);
  12.     exit();

Télécharger

SPIP

  1.     include_spip('inc/headers');
  2.    
  3.     $oauth_state = session_get('oauth_state');
  4.     $url = "http://oa.dnc.global/authorize?response_type=code&client_id=chemin_openid&scope=openid profile&redirect_uri=http://chemindeleau.com/callback_openid.php&state=$oauth_state";
  5.    
  6. redirige_par_entete($url);

Télécharger

Notes :
- Pour obtenir un jeton d’identité, le scope doit comporter "openid". Dans le cas contraire, la réponse sera identique à celle du protocole OAuth 2.0, et ne comprendra donc que le jeton d’accès.
- Pour obtenir un jeton de rafraîchissement (Refresh Token), le scope doit comporter "offline_access".
- Bien que la "norme" indique que le paramètre redirect_uri est obligatoire, il peut être omis si l’application cliente a été inscrite avec une seule adresse de retour.
Bien que la norme indique que le paramètre state est facultatif, il est essentiel pour la sécurité de le présenter dans la demande.
- si l’application cliente a été inscrite avec plusieurs adresses de retour, le paramètre redirect_uri est obligatoire, et doit représenter l’une d’elles.
- Il est possible de rajouter à l’URL tout paramètre utile. Ceux-ci seront retransmis dans le corps de la réponse, de façon quasi intégrale.
- Avant qu’elle puisse interagir dans un flux OpenID Connect, l’auteur doit inscrire l’application cliente sur le serveur OAuthSD avec les paramètres attendus par OpenID Connect.
- Il est de la responsabilité de l’application cliente d’assurer la bonne forme et la sécurité des valeurs transmises par les paramètres d’URL.

Authentification de l’utilisateur final

A l’appel du Point d’extrémité d’autorisation :
- le serveur OAuthSD redirige l’user-agent vers la page d’authentification (on reste dans le domaine du serveur d’autorisation).
- l’utilisateur final s’authentifie dans cette page (les identifiants sont donc confinés au niveau du serveur).
- le serveur poste le code d’autorisation au Point d’extrémité de redirection.

Voici un exemple de page d’authentification :

http://oa.dnc.global/authorize?response_type=code&client_id=chemin_openid&redirect_uri=http://chemindeleau.com/callback_openid.php&state=4f67ae45bd188aa3

Notes :
- si l’on souhaite afficher la page d’authentification dans le contexte visuel de l’application cliente, il faut le faire dans un iFrame, plutôt que par une insertion en script côté client (Popup Javascript), afin de ne pas compromettre les identifiants de l’utilisateur. Cependant, il convient d’appliquer les bonnes pratiques de sécurité relatives à la mise en oeuvre des iFrame.

Retour à l’application cliente

En cas de succès, le serveur redirige le navigateur sur le point d’extrémité de redirection dans l’application cliente ( par entête HTTP code 302 ). Cet URI est défini par l’auteur d’une application cliente quand il l’inscrit sur ce serveur. Voir : OpenID Connect : Lier une application cliente au serveur OAuthSD.

Les paramètres code et state sont passés dans l’URL. Exemple :

http://chemindeleau.com/callback_openid.php?code=3159339c2f1326f9fa128e161b8387feca690b65&state=98b3027139f7cb3be4a885d7c81b41bb

Il est de la responsabilité de l’application cliente d’assurer sa sécurité vis-à-vis des valeurs transmises par les paramètres d’URL.

Situations d’erreur

En cas d’échec le corps de la réponse contient :

index type valeur
page JSON Array error : titre de l’erreur,
error_description : description de l’erreur

La réponse HTTP ainsi que les valeurs de error et error_description sont données par le tableau suivant :

Réponse error
titre de l’erreur
error_description
description de l’erreur
Explication
302 login required The user must log in Le jeton d’accès n’existe pas ou n’est plus valide. Cette réponse n’apparait que si login=’none’ car, dans les autres cas, le serveur OAuthSD prend à sa charge le dialogue de connexion. Cette erreur pourra apparaître en cas d’échecs successifs.
302 interaction_required The user must grant access to your application Le serveur a déterminé que des scopes doivent être approuvés par l’utilisateur final. Il y a peu de chance que cette erreur apparaisse avec OAuthSD, car la situation est gérée au niveau du contrôleur Authorize qui prend à sa charge le formulaire d’acceptation.
302 consent_required The user denied access to your application L’utilisateur final a explicitement refusé la demande d’autorisation qui lui a été présentée.
400 invalid_client No client id supplied Le paramètre client_id est obligatoire.
400 invalid_client The client id supplied is invalid Le paramètre client_id doit être identique à ce qui a été inscrit pour l’application.
400 invalid_uri The redirect URI must not contain a fragment
400 invalid_uri No redirect URI was supplied or stored
400 invalid_uri A redirect URI must be supplied when multiple redirect URIs are registered
400 redirect_uri_mismatch The redirect URI provided is missing or does not match Le paramètre redirect_uri doit être identique à ce qui a été inscrit pour l’application.
400 invalid_uri The redirect URI must not contain a fragment
400 missing_code_challenge This application requires you provide a PKCE code challenge Voir Clé de vérification pour l’échange de code (PKCE)
401 not_allowed L’utilisateur final a probablement fait une erreur dans ses identifiants. Recommencer.
401 malformed_identifier L’utilisateur final a fourni un identifiant (e-mail ou pseudo) mal formé. Recommencer.
401 malformed_email Un E-mail est requis en guise d’identifiant de l’utilisateur final. L’utilisateur final a probablement fait une erreur. Recommencer.
403 consent_required The user denied access to your application L’utilisateur a refusé son accord ou a abandonné l’authentification.
403 forbidden Probablement une attaque de type "Man in the middle". Ne pas réagir.

Si le serveur retourne une erreur 401, il est de la responsabilité de l’application cliente de présenter un message d’erreur à l’utilisateur final et de recommencer (ou non) l’authentification. Dans tous les autres cas d’erreur, l’application cliente ne devrait pas redemander l’authentification avant d’avoir identifié l’erreur au niveau technique.

Dans certains cas, la redirection peut être effectuée, le corps de la réponse contenant un message d’erreur :

error
titre de l’erreur
error_description
description de l’erreur
Explication
invalid_request Invalid or missing response type response_type doit être "code"
unauthorized_client The grant type is unauthorized for this client_id Vérifiez que l’application a bien été inscrite avec le type d’autorisation Authorization_code
redirect_uri_mismatch The redirect URI is mandatory and was not supplied Le paramètre redirect_uri doit figurer dans la requête.

Réponse sans redirection

Un certain nombre d’erreurs provoquent une réponse HTTP directe (sans rediriger le navigateur sur le point d’extrémité de redirection). Il s’agit d’anomalies dans la requête, traduisant une tentative d’intrusion. Le code d’erreur HTTP est le plus souvent 400 ’Bad Request’ ou 403 ’Forbidden’.
Ce type d’erreur ne devrait jamais se produire avec une application cliente autorisée. Le concepteur d’une application cliente n’a pas à s’en préoccuper.

API OpenID Connect : Point d’extrémité de jeton (Token Endpoint)

  publié le par i-Tego WM

Pour obtenir les jetons, une application cliente s’adresse au point d’extrémité token avec le code obtenu dans la phase d’autorisation. Dans le cadre du protocole OpenID Connect, l’appel au contrôleur Token suit la demande d’authentification.

Point d’extrémité de jeton (Token Endpoint)

https://oa.dnc.global/token

Le point d’extrémité de jeton est le point d’extrémité sur le serveur d’autorisation auquel s’adresse l’application cliente avec le code d’autorisation.

Forme de la demande de jeton d’accès dans le cadre du flux Authorization Code Grant

La demande ne doit être effectuée que par la méthode POST.
Les paramètres suivants doivent être postés :
- grant_type : (OBLIGATOIRE) Type de flux d’autorisation, doit être "authorization_code".
- code : (OBLIGATOIRE) le code d’autorisation reçu.
- redirect_uri : (OPTIONNEL) l’adresse de retour à l’application cliente. Si cette valeur est fournie, elle doit correspondre à l’une des valeurs inscrites pour le client. Si cette valeur est omise, la valeur unique inscrite pour le client sera utilisée.

Pour l’authentification de l’application cliente auprès du serveur d’autorisation, OAuthSD accepte les méthodes ’client_secret_basic’ et ’client_secret_post’ [1].

Authentification du client par ’client_secret_basic’
L’authentification est effectuée en utilisant l’authentification HTTP Basic (cf. section 2.3.1 de OAuth 2.0 [RFC6749]).

Exemple de construction de l’entête Authorize avec PHP cURL :

PHP

  1. // Token request
  2. $data = array(
  3.     'grant_type' => 'authorization_code',
  4.     'code' => $code,
  5. );
  6. $h = curl_init(h = curl_init($oidc_token_endpoint);
  7. curl_setopt($h, CURLOPT_RETURNTRANSFER, true);
  8. curl_setopt($h, CURLOPT_TIMEOUT, 10);
  9. curl_setopt($h, CURLOPT_USERPWD, "{$client_id}:{$client_secret}");
  10. curl_setopt($h, CURLOPT_POST, true);
  11. curl_setopt($h, CURLOPT_HTTPHEADER, array('Content-Type: application/x-www-form-urlencoded'));
  12. curl_setopt($h, CURLOPT_POSTFIELDS, http_build_query($data));
  13. //curl_setopt($h, CURLOPT_SSL_VERIFYPEER, false);
  14.  
  15. $res1 = curl_exec($h);
  16. $errorc1 = curl_error($h);
  17.                

Télécharger

Authentification du client par ’client_secret_post’

Les identifiants client_id et client_secret qui ont été définis lors de l’inscription de l’application cliente sur le serveur sont passés dans le corps de la requête.

Exemple avec PHP cUrl :

PHP

  1. // Token request
  2. $data = array(
  3.     'client_id' => $client_id,
  4.     'client_secret' => $client_secret,
  5.     'grant_type' => 'authorization_code',
  6.     'code' => $code,
  7. );
  8. $h = curl_init($oidc_token_endpoint);
  9. curl_setopt($h, CURLOPT_RETURNTRANSFER, true);
  10. curl_setopt($h, CURLOPT_TIMEOUT, 10);
  11. curl_setopt($h, CURLOPT_POST, true);
  12. curl_setopt($h, CURLOPT_HTTPHEADER, array('Content-Type: application/x-www-form-urlencoded'));
  13. curl_setopt($h, CURLOPT_POSTFIELDS, http_build_query($data));
  14. //curl_setopt($h, CURLOPT_SSL_VERIFYPEER, false);
  15.  
  16. $res1 = curl_exec($h);
  17. $errorc1 = curl_error($h);
  18.                

Télécharger

Autres flux

Notons que OAuth 2.0 définit des flux effectuant un appel direct au contrôleur Token, voir : différents type de flux.

Notons également que le flux implicite défini dans le cadre d’OpenID Connect obtient directement les jetons du contrôleur Authorize.

Certains auteurs définissent dans le cadre d’OAuth 2.0 un flux "Refresh Token Grant" s’adressant au point d’extrémité de jeton. Il ne s’agit pas à proprement parler d’un flux puisque, prolongeant un flux d’autorisation établi précédemment, il ne peut exister de façon autonome et donc contredit la nature REST de l’API. Voir : Rafraîchir (actualiser) un jeton d’accès.

Réponse du serveur

En cas de succès, le serveur retourne une réponse HTTP 200. Le corps de la réponse contient :

index type valeur
page JSON array access_token : (string) jeton d’accès OAuth 2.0
expires_in : (long) durée de vie en secondes
token_type : (string) "Bearer"
scope : (string) "openid ... "
id_token : (string) jeton d’identification (JWT)

Le Header comporte, comme il se doit, la directive ’Cache-Control : no-cache, no-store’.

 

En cas d’échec, le corps de la réponse contient :

index type valeur
page JSON Array error : titre de l’erreur,
error_description : description de l’erreur

La réponse HTTP ainsi que les valeurs de error et error_description sont données par le tableau suivant :

Réponse error
titre de l’erreur
error_description
description de l’erreur
Explication
405 invalid_request The request method must be POST when requesting an access token La méthode de la demande doit être POST lorsque vous demandez un jeton d’accès. Notez que le Header contient ’Allow : POST’
400 invalid_request The grant type was not specified in the request Le type d’autorisation n’a pas été spécifié dans la demande
400 unsupported_grant_type Grant type "X" not supported Le Type d’autorisation "X" n’est pas supporté.
400 invalid_grant Authorization code doesn\’t exist or is invalid for the client Le code d’autorisation n’existe pas ou est invalide pour le client. Cette situation peut résulter du fait que le code a déjà été utilisé pour une demande de jeton.
400 unauthorized_client The grant type is unauthorized for this client_id Le type d’autorisation n’est pas autorisé pour ce client_id
400 invalid_scope The scope requested is invalid for this request La portée d’autorisation demandée est invalide pour cette demande
400 invalid_scope The scope requested is invalid for this client La portée d’autorisation demandée est valide pour ce client
400 invalid_scope An unsupported scope was requested La portée d’autorisation demandée n’est pas supportée

Demande du jeton de rafraîchissement

OpenID Connect ne retourne un jeton de rafraîchissement (Refresh Token), conjointement au jeton d’accès, que si le scope "offline_access" a été inclus dans la requête et accepté, ce qui ne se produira qu’avec le flux d’Autorisation via un code (Authorization Code Grant).

Notes

[1Dans l’état actuel du développement, les méthodes ’private_key_jwt’, ’client_secret_jwt’ et ’none’ ne sont pas implémentées de façon stable.

API Open ID Connect : Introspection (Introspection Endpoint)

  (publié initialement le mardi 3 janvier 2017) par i-Tego WM

L’introspection permet à une application cliente ou à un serveur de ressource (RS) de valider un jeton auprès du serveur d’authentification (AS) .

Les jetons soumis peuvent être du type Access Token, Identity Token (JWT) ou Json Web Encryption (JWE).

Implémentation de l’Introspection

Il n’y a pas (à ce jour) de "norme" définissant l’Introspection pour OpenID Connect. Cependant, OAuthSD, ainsi que les implémentations courantes, se fonde sur la proposition de standard RFC 7662 : OAuth 2.0 Token Introspection. DnC a proposé une fonction d’introspection pour la bibliothèque OAuth 2.0 PHP.

Proposition de standard RFC 7662

Traduction d’un extrait du document RFC 7662 : OAuth 2.0 Token Introspection

2.2. Réponse d’introspection

Le serveur répond avec un objet JSON [RFC7159] dans le format "application/json "avec les membres de niveau supérieur suivants.

active
CHAMPS OBLIGATOIRE. booléen indiquant si le jeton présenté est actuellement actif ou non. Les spécificités de l’état "actif" d’un jeton variera en fonction de la mise en œuvre du serveur d’autorisation et les informations qu’il conserve sur ses jetons, mais une "vraie"
valeur retournée pour la propriété "active" indiquera généralement qu’un jeton donné a été émis par ce serveur d’autorisation, n’a pas été révoqué par le propriétaire de la ressource et relève de sa fenêtre de validité donnée (par exemple, après son heure d’émission et avant son heure d’expiration). Voir la section 4 pour des informations sur la mise en œuvre de ces contrôles.

scope
OPTIONNEL. Une chaîne JSON contenant une liste de portées associées à ce jeton, dans le format décrit dans la Section 3.3 de OAuth 2.0 [RFC6749].

client_id
OPTIONNEL. Identifiant de client pour le client OAuth 2.0 qui a demandé ce jeton.

username
OPTIONNEL. Identifiant lisible par l’homme pour le propriétaire de la ressource qui a autorisé ce jeton.

token_type
OPTIONNEL. Type de jeton tel que défini dans la section 5.1 de OAuth 2.0 [RFC6749].

exp
OPTIONNEL. Horodatage entier, mesuré en nombre de secondes depuis le 1er janvier 1970 UTC, en indiquant la date d’expiration de ce jeton, comme défini dans JWT [RFC7519].

iat
OPTIONNEL. Horodatage entier, mesuré en nombre de secondes depuis le 1er janvier 1970 UTC, en indiquant quand ce jeton a été publié à l’origine, tel que défini dans JWT [RFC7519].

nbf
OPTIONNEL. Horodatage entier, mesuré en nombre de secondes depuis le 1er janvier 1970 UTC, en indiquant quand ce jeton ne doit pas être utilisé auparavant, comme défini dans JWT [RFC7519].

sub
OPTIONNEL. Sujet du jeton, tel que défini dans JWT [RFC7519].
Généralement, un identifiant lisible par machine du propriétaire de la ressource qui a autorisé ce jeton.

aud
OPTIONNEL. chaîne Identifiant spécifique au service ou liste de chaînes identifiants représentant le public visé pour ce jeton, comme défini dans JWT [RFC7519].

iss
OPTIONNEL. Chaîne représentant l’émetteur de ce jeton, sous la forme définie dans JWT [RFC7519].

jti
OPTIONNEL. Identificateur de chaîne pour le jeton, tel que défini dans JWT [RFC7519].

Des implémentations spécifiques PEUVENT étendre cette structure avec leurs propres noms de réponse spécifiques aux services en tant que membres de niveau supérieur de cet objet JSON. Les noms de réponse destinés à être utilisés sur plusieurs domaines DOIVENT être inscrit dans le registre "OAuth Token Introspection Response" défini à la section 3.1.

Le serveur d’autorisation PEUT répondre différemment à différentes ressources protégées faisant la même demande. Par exemple, un serveur d’autorisation PEUT limiter les portées d’un jeton donné retourné pour chaque ressource protégée pour empêcher une ressource protégée d’en apprendre davantage sur le réseau que nécessaire pour son fonctionnement.

La réponse PEUT être mise en cache par la ressource protégée pour améliorer les performances et réduire la charge sur le point final d’introspection, mais au prix de la qualité de la validité des informations utilisées par la ressource protégée pour prendre des décisions d’autorisation. Voir la section 4 pour plus d’informations en ce qui concerne le compromis lorsque la réponse est mise en cache.

DnC s’est inspiré de cette proposition de standard pour étendre les jetons acceptés aux trois types Access Token, Identity Token (JWT) ou Json Web Encryption (JWE).

Point d’extrémité d’introspection

Point d’extrémité d’introspection (Introspection Endpoint)

https://oa.dnc.global/introspect

Forme de la demande d’Introspection

La demande ne doit être effectuée que par la méthode POST.

Note :
- OAuthSD ne nécessite pas l’enregistrement d’un scope réservé pour autoriser le client à utiliser l’introspection, contrairement à d’autres implémentations. Le scope ’openid’ est également inutile, le controleur fonctionnant aussi bien dans le cadre de OAuth 2.0 que celui d’OpenID Connect.

Contrôle de l’accès

Les demandes adressées au point de terminaison d’introspection doivent être authentifiées avec les informations d’identification du client (Client Credentials Grant) ou autorisées avec un jeton d’accès au porteur (Bearer Token).
En conséquence, l’application appelante (ou le serveur de ressource) doit être enregistrée comme cliente sur le serveur d’authentification

Client Credentials Grant
C’est l’approche la plus simple et celle qui est recommandée.
L’application appelante (ou le serveur de ressource) doit être enregistrée comme cliente sur le serveur d’authentification [1].
L’authentification est effectuée en utilisant l’authentification HTTP Basic (cf. section 2.3.1 de OAuth 2.0 [RFC6749]). Les identifiants client_id et client_secret sont ceux qui ont été définis lors de l’inscription de l’application cliente sur le serveur.

Bearer Token
Cette approche nécessite un jeton d’accès pour autoriser la demande d’introspection.
Pour un serveur de ressource, cela est plus compliqué du fait de la durée limitée de validité du jeton d’accès, contraignant à une nouvelle demande de jeton. Une façon d’obtenir un tel jeton consiste à inscrire l’application pour le flux Client Credential Grant.
L’authentification est effectuée en passant le jeton dans l’en-tête Authorization de la demande d’introspection.

Note :
- OAuthSD permet de sauter cette étape en réglant la constante de configuration AUTHENTICATE_INTROSPECT_REQUEST à false.
De fait, la rfc indique que l’objectif de cette authentification client est "Pour empêcher les attaques par balayage de jetons ..."
Les attaques par balayage (scanning) pourraient être mieux atténuées de certaines autres manières, en particulier au niveau du réseau.
De plus, donner à un client inconnu des informations sur la validité du jeton n’est pas un problème de sécurité élevé.

Requête

Les paramètre suivants doivent être postés :

- token (OBLIGATOIRE) : le jeton à valider. Les jetons soumis peuvent être du type Access Token, Identity Token (JWT) ou Json Web Encryption (JWE).
Si un jeton JWE est reconnu, il est déchiffré et le processus se poursuit avec la charge utile du JWE, qui n’est autre que le JWT.

- requester_ip (OPTIONNEL) : Lorsque l’Introspection est demandée par une ressource protégée (distincte de l’application cliente à l’origine de l’authentification), il importe de ne pas répondre à un malware ayant intercepté le jeton et tentant de le ré-utiliser.
Pour cela, la ressource protégée doit transmettre l’IP du demandeur au moyen du paramètre ’requester_ip’.
La fonction d’introspection d’OAuthSD vérifie que l’IP indiquée est celle qui a été enregistré avec l’application cliente ou, à défaut, se trouve dans le sous réseau de l’application cliente tel qu’il peut être déterminé à partir de l’URL de retour.

Notes :
- OAuthSD ne nécessite pas le paramètre token_type.
- Avertissement à propos du paramètre ’alg’ : la RFC 7515, section 4.1.1 prévoit d’appliquer la valeur du paramètre ’alg’ pour le choix de l’algorithme de validation de la signature.
C’est une faille de sécurité sévère, et donc une erreur de la spécification. L’introspection d’OAuthSD applique la méthode définie pour chaque application, avec laquelle les jetons sont signés, quelle que soit la valeur de ’alg’ et génère une erreur si la valeur est différente.

Réponse du serveur

En cas de succès, le serveur retourne une réponse HTTP 200.

Le corps de la réponse contient un tableau portant les informations suivantes :

index type valeur
status entier code HTTP
headers string Headers de la réponse
page string JSON Array :
active : true,
scope : (JSON string Array) scopes associés au jeton.
client_id : ID de l’application cliente qui a demandé ce jeton.
username : ID OAuth de l’utilisateur final (human-readable).
exp : (long) (secondes depuis le 1° janvier 1970). Unix Time de la fin de validité du jeton.
iat : (long) (secondes depuis le 1° janvier 1970). Unix Time de création du jeton
iss : (string) issuer : serveur d’authentification qui a diffusé ce jeton.
sub : identifiant interne de l’utilisateur qui a autorisé ce jeton.
aud : audience définie pour ce jeton.

 

Si le jeton n’est pas valide, alors que la requête n’a pas échoué, l’introspection ne retourne un code HTTP 200 et une réponse active : false.

Note :
- Le traitement des erreurs d’introspection décrit dans ce qui suit est propre à cette version de OAuthSD, destinée au développement et à la mise au point d’une application mettant en oeuvre la délégation d’authentification. Conformément à la spécification, les serveurs OAuthSD de production ne donnent pas de détail sur l’erreur, mais retournent simplement un code HTTP 401 et une réponse active : false.

En cas d’échec de la requête, le corps de la réponse contient :

index type valeur
page string JSON Array :
error : titre de l’erreur,
error_description : description de l’erreur

La réponse HTTP ainsi que les valeurs de error et error_description sont données par le tableau suivant :

Réponse error
titre de l’erreur
error_description
description de l’erreur
Explication
400 invalid_request Only one method may be used to authenticate at a time (Auth header, GET or POST) La requête est mal formée
400 invalid_request Missing parameters : "token" is required La requête Introspection requiert le paramètre ’token’.
400 invalid_request When putting the token in the body, the method must be POST or PUT Si on place le token dans le corps de la requête, la méthode ne peut être que POST ou PUT
400 invalid_request The content type for POST requests must be "application/x-www-form-urlencoded l’IETF spécifie ce type de contenu. NB : tous les serveurs Web ne remplissent pas cette variable _SERVER voir http://tools.ietf.org/html/rfc6750#section-2.2
401 invalid_token JWT is malformed le jeton JWT ne peut être décodé.

Exemples

Demande de validation d’un jeton d’identité, méthode Auth Header (ou "JWT Bearer" ) :

PHP

  1. /*
  2. Autorisation avec OAuth Server by DnC
  3. OpenID Connect : Introspection, méthode Auth Header
  4. */
  5. function oauth_authorize($idtoken) {
  6.  
  7.     $Ok = false;
  8.  
  9.     if ( !empty( $idtoken) ) {
  10.  
  11.         $h = curl_init(AUTHENTICATION_SERVER_URL . 'introspect');
  12.         curl_setopt($h, CURLOPT_RETURNTRANSFER, true);
  13.         curl_setopt($h, CURLOPT_TIMEOUT, 10);
  14.         curl_setopt($h, CURLOPT_HTTPHEADER,
  15.             array('Authorization: Bearer ' . $idtoken));
  16.        
  17.         $response = curl_exec($h);
  18.      
  19.         if ( (int)curl_getinfo($h)['http_code'] === 200 ) {  
  20.             $jwt = json_decode($response, true);  
  21.             $Ok = ( $jwt['active'] == true );
  22.         }
  23.     }
  24.  
  25.     if ( $Ok AND isset($_SERVER["HTTP_REFERER"]) ) {
  26.         $urlParts = parse_url($_SERVER["HTTP_REFERER"]);
  27.         if ( $urlParts['host'] !== $_SERVER["HTTP_HOST"] ) {
  28.             // CORS : autoriser l'origine
  29.             $issuer = $urlParts['scheme'] . "://" . $urlParts['host'];
  30.             include_spip('inc/headers');    
  31.             header('Access-Control-Allow-Origin', $issuer);
  32.         }
  33.     }
  34.  
  35.     return $Ok;
  36.  
  37. }

Télécharger

Variante avec méthode GET pour SPIP :
SPIP

  1. /*
  2. Autorisation avec OAuth Server by DnC
  3. OpenID Connect : Introspection, méthode GET  
  4. */
  5.  
  6. function oauth_authorize($idtoken) {
  7.  
  8.     $Ok = false;
  9.  
  10.     if ( !empty( $idtoken) ) {
  11.  
  12.         // Interroger l'introspection de OAuth Server by DnC
  13.         include_spip('inc/distant');
  14.         $url = "http://oa.dnc.global/introspect?token=" . $idtoken;
  15.         $response = recuperer_url($url);  
  16.        
  17.         if ( (int)$response['status'] === 200 ) {
  18.             $jwt = json_decode($response['page'], true);
  19.             if ( $jwt['active'] == true ) {
  20.                 if ( isset($_SERVER["HTTP_ORIGIN"]) ) {
  21.                     // Accès HTTP (CORS) : autoriser l'origine
  22.                     include_spip('inc/headers');
  23.                     $issuer = trim(strtr($_SERVER["HTTP_ORIGIN"], '<>"\'', '[]##'));    
  24.                     header('Access-Control-Allow-Origin', $issuer);
  25.                 }
  26.                 $Ok = true;
  27.             }
  28.         }
  29.  
  30.     }
  31.  
  32.     return $Ok;
  33.  
  34. }

Télécharger

Un exemple plus complet, faisant apparaître la totalité des erreurs possibles, figure ici : OpenID Connect : Exemples complets du flux d’Autorisation via un code puis requête UserInfo.

Notes :

- Dans le cas où l’application cliente et le serveur de données protégées se trouvent dans des domaines différents, il faut gérer l’autorisation HTTP, comme cela est fait dans l’exemple ci-dessus. Voyez Contrôle d’accès HTTP (CORS).

- L’interrogation du serveur d’autorisation à chaque accès d’une ressource protégée peut le surcharger. Pour éviter cela, on peut mettre en cache la réponse du serveur du côté du serveur de ressource. Avec SPIP, c’est le rôle de la fonction recuperer_url_cache() qui pourra remplacer recuperer_url() dans l’exemple précédent. La fonction permet de régler le délai de garde en cache, qu’il convient de fixer à une durée assez courte (10 secondes par exemple), l’essentiel étant de ne pas bombarder le serveur. Voici un exemple :

SPIP

  1. $res = recuperer_url_cache( $url, array('delai_cache' => 10) ); // Délai de 10s

La fonction décrite précédemment peut être utilisée dans une fonction d’autorisation d’accès à un objet SPIP ( ici l’objet gis de radar ) :

SPIP

  1. function _autoriser_gis($faire, $quoi, $id, $qui, $options) {
  2.     if ( $qui['statut'] == '0minirezo' ) {
  3.         // Toujours autoriser un administrateur
  4.         return true;
  5.     } else {
  6.         if ( $idtoken = $_GET['token'] ) {
  7.             // Vérifier le jeton d'accès
  8.             return oauth_authorize($idtoken);    
  9.         } else return false;
  10.     }
  11. }

Télécharger

Il faut cependant noter que la mise en cache expose à la réutilisation du jeton par un malware.

Exemple d’appel, avec le jeton passé par la méthode Auth Header et le paramètre ’requester_ip’ par Post :
PHP

  1.             // Method Bearer + parameters by Post
  2.             $data = array(
  3.                 'requester_ip' => $_SERVER['SERVER_ADDR'],
  4.             );
  5.             $authorization = "Authorization: Bearer ". $res1['id_token'];
  6.             $h = curl_init($introspection_endpoint);
  7.             curl_setopt($h, CURLOPT_HTTPHEADER, array(
  8.                 'Content-Type: application/x-www-form-urlencoded',
  9.                 'Authorization: Bearer ' . $id_token
  10.             ));
  11.             curl_setopt($h, CURLOPT_RETURNTRANSFER, true);
  12.             curl_setopt($h, CURLOPT_POST, 1);  
  13.             curl_setopt($h, CURLOPT_POSTFIELDS, http_build_query($data));
  14.             ...

Télécharger

Avantages et inconvénients de l’Introspection

La validation du jeton d’identité (ID Token) auprès du serveur d’authentification (introspection) présente quatre avantages importants :
- elle permet de savoir si le jeton a été révoqué, contrairement à la validation locale ; c’est là un avantage fondamental pour la sécurité : si vous décidez de révoquer un jeton d’accès, alors il faut bien que les applications réagissent, le seul moyen est l’introspection.
- cette méthode ne nécessite pas de connaître la clé publique de l’application cliente pour valider la signature du jeton, ce qui permet à des serveurs de ressource étrangers à l’organisation de valider les jetons reçus ;
- on peut obtenir des informations sur l’utilisateur final (qui est à l’origine de l’autorisation) qui permettent d’identifier cet utilisateur et, donc, d’agir en fonction de la confiance à accorder d’après son profil ;
- il est possible de mettre en œuvre la déclaration "jti" (JWT ID) qui permet au serveur d’autorisation de vérifier que le jeton n’a pas déjà été utilisé ;
- OAuthSD propose également la vérification de l’IP du demandeur : si une ressource protégée a transmis l’adresse IP de son propre demandeur avec le paramètre ’requester_ip’, on vérifie que cette adresse IP se trouve dans le sous-réseau de l’application cliente identifiée par la déclaration ’aud’. Cela est essentiel pour ne pas répondre à un malware ayant intercepté le jeton.

Elle a pour seul inconvénient d’augmenter le trafic avec le serveur d’autorisation.

Remarque quant au lien entre jeton d’identité et jeton d’accès et l’intérêt réel de ce dernier

Justin Richer : (openid-specs-ab)

"Il n’y a pas de relation 1:1 entre le jeton d’accès et jeton d’identité (ID Token), surtout si vous considérez que le jeton d’accès peut être actualisé ou ne pas expirer, alors que le jeton d’identité doit expirer. Le ID Token représente l’événement authn (et la session, dans une certaine mesure), alors que le jeton d’accès représente une autorisation déléguée d’accès aux informations de profil de l’utilisateur. Vous pouvez obtenir des informations sur l’événement d’authentification qui a généré le jeton d’accès à partir de l’introspection, mais il est important de se rappeler que le jeton d’accès n’est pas destiné à être étroitement lié à cet événement d’authentification. En fait, c’est toute la question d’OAuth qui lie de façon lâche l’authentification au jeton d’accès et maintient le jeton d’accès valide alors que l’utilisateur n’est plus là.

Cette remarque à propos de la faiblesse du lien entre jeton d’accès et jeton d’identité rejoint la conclusion émise à propos de la requête Userinfo : toute requête Userinfo devrait suivre le cycle : demande d’autorisation, validation du ID Token, demande Userinfo, vérification de la concordance des user_id. C’est ce qui est décrit dans les exemples.

Que vaut réellement un jeton d’accès si :
- on ne peut vérifier sa validité sans introspection [2] (autant alors faire l’introspection pour le jeton d’identité),
- on n’est pas certain du lien avec le jeton d’identité ?

Plus généralement, ne devrait-on pas conclure ainsi : plutôt que d’utiliser le jeton d’accès ne vaudrait-il pas mieux utiliser exclusivement le jeton d’identité ? C’est pour cela qu’il existe un flux OpenID Connect ne retournant que l’ID Token.

Notes

[1OAuthSD autorise tous les clients enregistrés à accéder au point de terminaison d’introspection. Il est donc inutile de spécifier un scope ou une métadonnée particuliers.

Demande d’informations sur l’utilisateur (UserInfo Endpoint)

  publié le par i-Tego WM

Le point d’extrémité UserInfo du protocole OpenID Connect permet d’obtenir des informations sur l’utilisateur final authentifié.

Point d’extrémité UserInfo

L’URI est la suivante :

https://oa.dnc.global/userinfo

Contrôle de l’accès

Les demandes adressées au point de terminaison UserInfo doivent être authentifiées avec les informations d’identification du client (Client Credentials Grant) ou autorisées avec un jeton d’accès au porteur (Bearer Token).
En conséquence, l’application appelante (ou le serveur de ressource) doit être enregistrée comme cliente sur le serveur d’authentification.
S’agissant d’une spécification d’OpenID Connect, l’application appelante doit avoir été enregistrée avec le scope réservé ’openid’.

Client Credentials Grant
C’est l’approche la plus simple.
L’application appelante (ou le serveur de ressource) doit être enregistrée comme cliente sur le serveur d’authentification.
L’authentification est effectuée en utilisant l’authentification HTTP Basic (cf. section 2.3.1 de OAuth 2.0 [RFC6749]). Les identifiants client_id et client_secret sont ceux qui ont été définis lors de l’inscription de l’application cliente sur le serveur.

Bearer Token
Cette approche nécessite un jeton d’accès pour autoriser la demande d’introspection.
Pour une application ce ne sera pas un problème dans la mesure où la demande UserInfo suivra généralement une demande de jetons.
Pour un serveur de ressource, cela est plus compliqué du fait de la durée limitée de validité du jeton d’accès, contraignant à une nouvelle demande de jeton. Une façon d’obtenir un tel jeton consiste à inscrire le serveur de ressource pour le flux Client Credential Grant.
L’authentification est effectuée en passant le jeton dans l’en-tête Authorization de la demande d’introspection.

Forme de la demande UserInfo

La méthode de transmission recommandée pour la transmission du jeton d’accès à UserInfo est HTTP Auth Header :

 GET /userinfo HTTP/1.1
 Host: oa.dnc.global/userinfo
 Authorization: Bearer SlrqTTP7i88GkKGQWDbPs9RWVAnWHz5e7nxAV32hH

Exemples :

PHP

  1. // Méthode Auth Header
  2.  
  3.     $h = curl_init('https://oa.dnc.global/userinfo');
  4.     curl_setopt($h, CURLOPT_RETURNTRANSFER, true);
  5.     curl_setopt($h, CURLOPT_TIMEOUT, 10);
  6.     curl_setopt($h, CURLOPT_HTTPHEADER, array('Authorization: Bearer ' . $access_token));
  7.  
  8.     $res = curl_exec($h);
  9.     if (!$res)
  10.         exit(curl_error($h));
  11.  
  12.     curl_close($h);
  13.     $res = json_decode($res, true);
  14.  
  15.     echo "UserInfo Response:\n";
  16.     print_r($res);

Télécharger

SPIP

  1. $url = "https://oa.dnc.global/userinfo";
  2. $options = array(
  3.     'methode' => 'GET',
  4.     'datas' => 'Authorization: Bearer ' . $access_token,
  5. );
  6. $resource_response = recuperer_url($url, $options);

Télécharger

Mais on peut également transmettre le jeton d’accès avec la méthode POST. Notez dans l’exemple suivant que le jeton d’accès est fourni sans l’indication ’Bearer’ :
PHP

  1. // Méthode Post
  2.  
  3.     $data2 = array(
  4.         'access_token' => $access_token,
  5.     );
  6.     $h = curl_init($userinfo_endpoint);
  7.     curl_setopt($h, CURLOPT_RETURNTRANSFER, true);
  8.     curl_setopt($h, CURLOPT_TIMEOUT, 10);
  9.     curl_setopt($h, CURLOPT_POST, true);
  10.     curl_setopt($h, CURLOPT_HTTPHEADER, array('Content-Type: application/x-www-form-urlencoded'));     // semble facultatif : curl le fait pour nous?
  11.     curl_setopt($h, CURLOPT_POSTFIELDS, http_build_query($data2));
  12.     //*/

Télécharger

Réponse du serveur

En cas de succès, le serveur retourne une réponse HTTP 200.

Le corps de la réponse contient un tableau portant les informations suivantes :

index type valeur
status entier code HTTP
headers string Headers de la réponse
page string JSON Array :
La réponse est détaillée ici : Réponse UserInfo.

Note :
- La valeur (string) "null" est forcée pour tous les champs vides ou NULL.
- La spécification indique qu’il n’est pas garanti que la réponse UserInfo corresponde à l’utilisateur identifié par l’élément user_id du ID Token ID. Avant de considérer la réponse UserInfo comme valide, il convient de vérifier que la revendication user_id dans la réponse UserInfo Endpoint correspond exactement à la revendication user_id dans le Token ID.
- Par ailleurs, la spécification impose de vérifier un ID Token dès sa réception. Ceci veut dire que toute requête Userinfo devrait suivre le cycle : demande d’autorisation, validation du jeton ID Token, demande Userinfo, vérification de la concordance des user_id. C’est ce qui est décrit dans les exemples.
- Si la constante de configuration CHECK_CLIENT_IP est réglée à ’true’ (ce qui est fortement conseillé), et que la valeur de l’IP a été fournie lors de l’enregistrement de l’application cliente (ce qui est également conseillé), le contrôleur UserInfo vérifiera que l’IP de l’origine de la requête est conforme. Cela interdit à une application étrangère d’utiliser un jeton d’accès volé.

 

En cas d’échec, le corps de la réponse contient :

index type valeur
page string JSON Array :
error : (string) titre de l’erreur,
error_description : (string) description de l’erreur

La réponse HTTP ainsi que les valeurs de error et error_description sont données par le tableau suivant :

Réponse error titre de l’erreur error_description description de l’erreur Explication
400 invalid_request Only one method may be used to authenticate at a time (Auth header, GET or POST) N’utiliser qu’une seule méthode d’authentification à la fois.
400 invalid_request Malformed auth header La requête de type Auth header est mal formée
400 invalid_request When putting the token in the body, the method must be POST or PUT Si on place le token dans le corps de la requête, la méthode ne peut être que POST ou PUT
400 invalid_request The content type for POST requests must be "application/x-www-form-urlencoded l’IETF spécifie ce type de contenu. NB : tous les serveurs Web ne remplissent pas cette variable _SERVER voir http://tools.ietf.org/html/rfc6750#section-2.2
401 (vide) (vide) La requête ne comporte aucune authentification
401 invalid_token The access token provided is invalid Le jeton ne figure pas dans le tokenStorage du serveur. Très probablement une tentative de violation d’accès.
401 expired_token The access token provided has expired Le jeton a expiré. L’application doit obtenir un nouveau jeton et relancer la requête
401 malformed_token Malformed token (missing "expires"
401 malformed_token Malformed token (missing "client_id")
403 insufficient_scope The request requires higher privileges than provided by the access token L’application cliente n’a pas été inscrite avec le scope openid

API OpenID Connect : Logout (Logout Endpoint)

  publié le par i-Tego WM

Le point d’extrémité logout permet la "déconnexion unique" (Single Logout, SLO) à partir d’une application (front channel single logout, RP generated logout etc.) : toutes les connexions d’un utilisateur sur les différentes applications sont invalidées.

Importance de la déconnexion unique pour la sécurité

La déconnexion unique (SLO) est la contrepartie de la connexion unique (SSO). L’authentification unique améliore l’expérience de l’utilisateur en minimisant le nombre de procédures de connexion et en lui permettant d’avoir des connexions ouvertes sur différentes applications sans avoir à fournir les informations d’identification à chaque fois.

Ce faisant, il existe un risque que l’utilisateur omette de se déconnecter d’une ou plusieurs applications, ce qui pourrait être exploité par un utilisateur malveillant. Avec la déconnexion unique, l’utilisateur peut mettre fin à toutes les sessions sans avoir à se déconnecter activement de chaque application, même si elles ont été ouvertes sur différents postes de travail ou mobiles. En conséquence, une application implémentant la déconnexion unique protège les utilisateurs et leurs données car il garantit qu’il ne reste aucune connexion active.

Implémentation de la déconnexion unique

ll n’y a pas à ce jour (fin 2018) de "norme" définissant la déconnexion pour OpenID Connect. Pour s’en convaincre, il suffit de voir cet article très complet : OpenID Connect Logout et de considérer l’état d’avancement de la proposition de norme : OpenID Connect Session Management. Les nombreuses références à des propositions de standard (drafts) et la complexité des solutions proposées montrent la difficulté dans laquelle se trouve la communauté Openid pour adopter une solution. DnC propose pour OAuthSD une solution de déconnexion centralisée particulièrement simple s’appuyant sur le cookie SLI.

La déconnexion est générale (Single Logout, SLO) : toutes les connexions d’un utilisateur sur différentes applications, différentes machines et quelque soit le lieu, sont invalidées.

Point d’extrémité de déconnexion unique (Single Logout Endpoint)

https://oa.dnc.global/logout

Forme de la demande de déconnexion unique

Une application effectue une demande de déconnexion en passant le jeton d’identité.

Le jeton est passé avec le paramètre "token" par l’une des méthodes suivantes : Auth Header, GET ou POST. Se reporter à API Open ID Connect : Introspection (Introspection Endpoint) pour la description de l’appel.

Exemple d’appel :

  1. if ( $jwt['active'] == 'true' ) {
  2.  
  3.     // Post Methode  
  4.     $data1 = array(
  5.         'token' => $id_token,
  6.     );
  7.  
  8.     // Effectuer un logout
  9.     $h = curl_init($logout_endpoint);
  10.     curl_setopt($h, CURLOPT_RETURNTRANSFER, true);
  11.     curl_setopt($h, CURLOPT_TIMEOUT, 10);
  12.     curl_setopt($h, CURLOPT_POST, true);
  13.     curl_setopt($h, CURLOPT_HTTPHEADER, array('Content-Type: application/x-www-form-urlencoded'));  
  14.     curl_setopt($h, CURLOPT_POSTFIELDS, http_build_query($data1));
  15.  
  16.     $res = curl_exec($h);
  17.     curl_close($h);
  18.  
  19.     if  ( empty($res['error'] ) ) {
  20.         // Continuer avec la déconnexion locale
  21.         ...
  22.     } else {
  23.         // ne pas déconnecter localement, signaler l'erreur.
  24.         ...
  25.     }
  26.  
  27. } else
  28.     // JWT is inactive
  29.     exit('Error : Invactive ID Token');
  30. }

Télécharger

Réponse du serveur

En cas de succès, le serveur retourne une réponse HTTP 200. Tous les jetons d’accès enregistrés sur le serveur pour l’utilisateur final connecté à l’application sont effacés. De ce fait, les cookies SLI éventuellement présents sur les agents de l’utilisateur seront détruits si une tentative de connexion est effectuée.

Notons que, contrairement au mécanisme de SLI qui est attaché à un agent utilisateur (un navigateur sur un poste de travail), la déconnexion unique s’applique à tous les agents que l’utilisateur aurait utilisés pour se connecter à des applications puisque tous les jetons d’accès enregistrés pour cet utilisateur seront effacés.

NoPassConnect tire parti de cette fonctionnalité en permettant la déconnexion à distance à l’aide d’un mobile.

En cas d’échec de la requête, le serveur n’effectue pas la déconnexion et retourne une réponse HTTP 403.

Connaître l’état de connexion d’une application

Si on souhaite connaître l’état actuel de connexion d’une application, il suffit de répéter la demande d’authentification avec prompt = ’none’. Si la déconnexion a eu lieu, le serveur répondra avec l’erreur ’login_required’ et un code HTTP 403. Cette solution a l’avantage de répondre totalement à la norme OpenID Connect dans son état définitif.

Discussion, alternative

Au reçu du code HTTP 200, l’application cliente à partir de laquelle a été émis la déconnexion pourra procéder à la déconnexion locale (probablement effacer des données de session et détruire un cookie de connexion etc.).

La question se pose pour les autres applications clientes que l’utilisateur aurait connectées précédemment.

Ces applications seront déconnectées (ne pourront se reconnecter automatiquement par Silent Re-Authentication (SRA)) à la fin de la validité de leur session locale. Même si elles apparaissent connectées, les requêtes qu’elles tenteront d’effectuer en direction des ressources protégées par le serveur OIDC n’aboutiront pas. Le retour d’erreur en résultant permettra au concepteur de l’application de prévoir une déconnexion locale.

Si la durée de la session locale de l’application cliente est courte, on aboutira à une déconnexion dans un délai court. A la limite, il existe des application sans session qui interrogent le serveur OIDC à chaque sollicitation pour authentifier la requête qui leur est faite. Les applications mono page et les services HTTP REST (sant état) appartiennent à cette catégorie.

Quoiqu’il en soit, le concepteur d’une application devrait intégrer un mécanisme de suivi de la validité du jeton d’accès pour informer l’utilisateur local et déconnecter localement l’application si le jeton est périmé. C’est l’objet de notre Monitoring de l’état de l’authentification et SLO. Le dispositif de Logout rentre ainsi dans le cadre du management de session.

Une alternative à notre méthode de déconnexion est décrite ici : OpenID Connect Session Management. Bien qu’il soit inscrit dans ce projet de spécification que la connaissance de l’état de connexion actuel d’une application (pour faire apparaître la déconnexion) peut être fait "sans générer de trafic sur le réseau", il n’en est rien car on "interroge l’OP ... à un intervalle approprié". La technique introduit donc un délai d’apparition de la déconnexion.

Notre méthode de déconnexion apparaît donc très intéressante, notamment si on considère sa simplicité de mise en oeuvre au regard des importantes modifications à apporter aux applications requises par le management de session OpenID Connect dont l’avenir, de plus, nous parait mal assuré.

API OpenId Connect : Point d’extrémité d’informations sur les clefs (Keys Endpoint)

  (publié initialement le mardi 25 septembre 2018) par i-Tego WM

Le protocole OpenID Connect utilise un jeton d’identité (ID Token) fondé sur JWT. La norme décrit comment une application cliente doit valider un ID Token reçu en réponse à une demande d’authentification. Cette vérification peut se faire localement (ou sinon par introspection), ce qui nécessite d’accéder aux informations sur les clés publiques.

C’est ce que permet le

Point d’extrémité d’informations sur les clefs (Keys Endpoint)

https://oa.dnc.global/keys

Forme de la demande d’informations sur les clés

La demande ne doit être effectuée que par la méthode POST.

Le client doit inclure ses informations d’authentification telles que décrites à la section 2.3. du document [RFC6749].

Exemple de requête :

POST /keys HTTP/1.1
    Host: oa.dnc.global
    Content-Type: application/x-www-form-urlencoded
    Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW

Réponse

En cas de succès le serveur retourne une réponse HTTP 200.
Le corps de la réponse contient une liste de définitions des clés publiques.

Chaque définition de clé est représentée sous un format JSON telle que défini dans le document JSON Web Key (JWK).

Exemple
Voici un exemple de réponse au format JSON :
[JSON]

{"keys":[{
"kid":"618584200ef916a154008d898a1e7edc",
"kty":"RSA",
"alg":"RS256",
"use":"sig",
"e":"AQAB",
"n":"ykcWIXjQ-f61XCJutT4JcgpmmobtB0U7ZcejT8tBD8rOZPkQDYf0Q3pMjCkNT8RRKzMYtkelY2CNn3U7kVJMgbJAtvZsCdlChVHAKvRnjwh1GR_6Zpmajm5cuz4bjQWWUIPIoXe_4JbC8nCrHdaagzB_6PrV_NILyn5unG1RLOrWx7_yzLaterDKxHTCBeOlqv_5VGFey0Ecf-X7Bj8YRx6fpamK4BcEAZSAbZMtAnTckp3hOYJgZo3MOXDxSQw1YR83i5Udcoaf7sxfhEA_b7r9CeNfgj76MKM7sdCfBMI7_JSz-YU_pJKCuT9Ny3IJQ0fQHpDzSq2oD_3cDcLjfXTGM67rXElwr9l8yrSNa29UGK4q2u9cFCQmJGlxVhZU6bzs7l4202LTJdPlzm_29jwLVvtqnVJSovMLHx84ReFtus1RdKRGB2plDQccvBNvp92D9lOnM3bAu1fKRAJwNh3hg1d6k7MVCHxoo9HVnkxzW48rAAJE2nk44a2Y0cclufBhvKRdNavldS1XOyZ_qf3qCAzsuYF1VAga8I-QOb6OyXp0KGLptbyYD-ZXISGPw3pDD3aAof_PMfFhSB96GHDnm-UCRpFHndQ_fZgtZhWugU8z22rV-irYCySqVkpE0ToWbNXNFZ9Jo1GXdwkpi1WjB7S-ipjzRFOlxhwbvZ0"},
...

kid

Comme différentes applications clientes peuvent utiliser la même paire de clé publique/privée, les clés sont indexées par un identifiant "kid" plutôt que par un client_id.

Cet identifiant est notamment présent dans la charge utile du jeton JWT, ce qui permettra de sélectionner la clé publique nécessaire pour valider la signature du jeton JWT.

jwks_uri, fichier jwks.json

La norme prévoit que le document de découverte .well-known/openid_configuration définisse une constante jwks_uri donnant l’URL du fichier jwks.json, sous la forme :
"jwks_uri"        : "https://oa.dnc.global/jwks/jwks.json",

Cette URL permet d’obtenir la même information que par le Point d’extrémité d’informations sur les clefs. Cependant, ces données sont statiques, alors que l’appel au contrôleur Keys recalcule le fichier jwks.json à chaque fois afin de prendre en compte un éventuel changement.

UpdatekeysController

OAuthSD propose un point d’entrée "updatekeys" qui provoque le re-calcul des paires de clés publiques-privées de tous les clients ainsi que la mise à jour des informations sur les clés.
Ce contrôleur n’est accessible qu’à partir de la même adresse que le serveur et sera donc soit lancé localement par le Superviseur soit par une tâche CRON à l’aide de wget.

 

Notes :
- Outre le fichier jwks.json, le dossier /jwks contient un fichier au format JSON pour chaque clé publique, sous le nom <kid>.json.
- Compte-tenu de la charge de calcul nécessaire à l’établissement du fichier, l’appel au point d’extrémité Keys doit être réservé à des applications de confiance ou être protégé contre les attaques de type déni de service.
- Pour éviter le recalcul du fichier jwks.json à chaque appel de Keys, il est possible de modifier la règle de réécriture dans le fichier .htaccess pour :
RewriteRule ^keys   /jwks/jwks.json [QSA,L]
- Une clé publique peut également être lue au moyen de l’API HTTP REST + TreeQL.

API OpenID Connect : Découverte

  publié le par i-Tego WM

Le protocole OpenID Connect nécessite l’utilisation de plusieurs points d’extrémité pour authentifier les utilisateurs et pour demander des ressources, y compris des jetons, des informations sur l’utilisateur et des clés publiques.

Comment un serveur de ressource indépendant du serveur d’autorisation peut-il connaître les fonctionnalités du serveur et y accéder ?

Le document de découverte

Conformément à la spécification OpenID Connect Discovery 1.0, OAuthSD expose ses métadonnées à l’URI :

https://oa.dnc.global/.well-known/openid-configuration

Voici un exemple (non stable) des métadonnées fournies au format JSON :

[JSON]

{
 "issuer"                                : "https://oa.dnc.global",
 "token_endpoint"                        : "https://oa.dnc.global/token",
 "introspection_endpoint"                : "https://oa.dnc.global/introspect",
 "revocation_endpoint"                   : "https://oa.dnc.global/revoke",
 "authorization_endpoint"                : "https://oa.dnc.global/authorize",
 "userinfo_endpoint"                     : "https://oa.dnc.global/userinfo",
 "jwks_uri"                              : "https://oa.dnc.global/keys",
 "scopes_supported"                      : [ "openid",
                                             "profile",
                                             "email",
                                             "address",
                                             "phone"],
 "response_types_supported"              : [ "code",
                                             "id_token",
                                             "token id_token",
                                             "id_token token",
                                             "code id_token" ,
                                             "code token id_token" ],
 "response_modes_supported"              : [ "query",
                                             "fragment",
                                              "form_post" ],
 "grant_types_supported"                 : [ "authorization_code",
                                             "refresh_token",
                                             "password",
                                             "client_credentials",
                                             "urn:ietf:params:oauth:grant-type:jwt-bearer"],      
 "code_challenge_methods_supported"      : [ "S256",
                                             "plain" ],
 "subject_types_supported"               : [ "public" ],                                
 "token_endpoint_auth_methods_supported" : [ "client_secret_basic",
                                             "client_secret_post",
                                             "client_secret_jwt",
                                             "private_key_jwt" ],                                
 "token_endpoint_auth_signing_alg_values_supported" :
                                           [ "HS256",
                                             "HS512",
                                             "HS384",
                                             "RS256",
                                             "RS384",
                                             "RS512"],                                
 "id_token_signing_alg_values_supported" : [ "RS256",
                                             "RS384",
                                             "RS512",
                                             "HS256",
                                             "HS384",
                                             "HS512" ],
 "userinfo_signing_alg_values_supported" : [ "RS256",
                                             "RS384",
                                             "RS512",
                                             "HS256",
                                             "HS384",
                                             "HS512" ],  
 "claim_types_supported"                 : [ "normal" ],  
 "claims_supported"                      : [ "sub",
                                             "iss",
                                             "auth_time",
                                             "acr",
                                             "name",
                                             "given_name",
                                             "family_name",
                                             "nickname",
                                             "email",
                                             "email_verified" ],
 "ui_locales_supported"                  : [ "fr" ],  
 "claims_parameter_supported"            : true,  
 "request_parameter_supported"           : false,
 "request_uri_parameter_supported"       : false,
 "require_request_uri_registration"      : false

Voir également :
- API OpenId Connect : Point d’extrémité d’informations sur les clefs (Keys Endpoint)