i-TegoDocumentationAuthentification > OpenID Connect OAuth Serveur dédié > Développer > Gestion du jeton d’accès

A propos du jeton d’accès

A plusieurs occasions nous avons exprimé une préférence pour le jeton d’Identité (ID token). Peut-être avons-nous donné l’impression que le jeton d’accès n’avait plus d’utilité.

Il convient de faire une remarque importante en faveur du jeton d’accès (par rapport au jeton d’identité) : le jeton d’accès suffit au serveur d’authentification (OP) pour identifier l’application cliente et l’utilisateur connecté. En effet, même si le jeton d’accès est opaque pour les applications clientes, il est enregistré dans la base de données du serveur OAuthSD (table access_token) avec l’ID du client (champ client_id) et l’ID de l’utilisateur connecté (champ user_id), sans oublier la date limite de validité (champ expires). On se réfère à ces données pour établir le fait que l’utilisateur est connecté à l’application ou non.

Donc, le jeton d’accès est utile pour toutes les applications le retournant au serveur pour vérification de la connexion de l’utilisateur (par exemple pour utiliser la session de l’utilisateur sur le serveur d’authentification, pour gérer les sessions d’une application à page unique (SPA), effectuer le monitoring etc.).

Bien que le jeton d’accès soit opaque pour le client et ne soit pas signé, il existe une méthode de vérification locale au moyen de la déclaration at_hash du jeton d’identité transmis conjointement par OpenID Connect. Notons également que le jeton d’accès peut prendre la forme d’un Json Web Token et être, de ce fait, signé.

OAuth 2.0 : Obtenir un jeton d’accès

  publié le par DnC

L’application cliente doit disposer d’un jeton d’accès pour obtenir des données protégées de la part d’un serveur de ressources.

Au préalable, l’application doit avoir obtenu un jeton d’autorisation auprès du serveur (point de terminaison authorize).

Le code nécessaire est entièrement à la charge de l’auteur de l’application cliente, en réaction à la redirection sur l’URI du point d’extrémité de redirection. La méthode dépend du flux d’autorisation : avec Authorization Code Grant, l’application cliente doit d’abord obtenir un code d’autorisation.

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

https://oa.dnc.global/oauth/token.php

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, l’ID client et le secret, pour obtenir un jeton d’accès.

C’est également à ce point qu’une application s’adresse pour Rafraîchir (actualiser) un jeton d’accès.

Forme de la demande de jeton d’accès

La demande ne doit être effectuée que par la méthode POST.
Les paramètres suivants doivent être postés :
- grant_type : Type de flux d’autorisation, par exemple "authorization_code".
- code : le code d’autorisation reçu.
- client_id : l’ID de l’application cliente.
- client_secret : le secret de l’application cliente.

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

Les valeurs données pour les paramètres grant_type, client_id et client_secret doivent correspondre aux valeurs définies lors de l’inscription de l’application cliente sur le serveur.

Notes :
- Il existe une façon de demander un jeton sans transmetre les identifiants de l’application cliente : Demande d’autorisation avec JWT (JWT Bearer Authorization Grant).

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
page JSON array access_token : jeton d’accès

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 X doesn\’t exist or is invalid for the client Le type d’autorisation indiqué n’existe pas ou est invalide pour le client
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

Exemples de code

Données de la requête :
- $AuthCode est le code d’autorisation obtenu à l’étape précédente et transmis à la page de CallBack
- $client_id, $client_secret : Comme indiqué lors de l’inscription de l’application cliente sur le serveur d’autorisation.

PHP

  1. // Demander un jeton d'accès pour l'application
  2.     $jeton = '';
  3.  
  4.     $url = 'https://oa.dnc.global/oauth/token.php';
  5.  
  6.     $datas =  array(
  7.         'grant_type' => 'authorization_code',
  8.         'code' => $AuthCode ,
  9.         'client_id' => 'XXXXXX',                              
  10.         'client_secret' => 'XXXXXXXXXXX',  
  11.     );        
  12.  
  13.     $ch = curl_init();
  14.  
  15.     curl_setopt($ch, CURLOPT_URL, $url);
  16.     curl_setopt($ch, CURLOPT_POST, true);
  17.     curl_setopt($ch, CURLOPT_POSTFIELDS, $datas);
  18.     curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
  19.     curl_setopt($ch, CURLOPT_HEADER, false);
  20.     curl_setopt($ch, CURLOPT_TIMEOUT, 30);
  21.     curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
  22.  
  23.     $result_json = curl_exec($ch);
  24.     $result = json_decode($result_json, true);
  25.  
  26.     $token = $result['access_token'];
  27.  
  28.     curl_close($ch);

Télécharger

SPIP

  1.         $jeton = '';
  2.         $url = 'https://oa.dnc.global/oauth/token.php';
  3.         $options = array(
  4.             'method' => 'POST',
  5.             'datas' =>  array(
  6.                 'grant_type' => 'authorization_code',
  7.                 'code' => '$AuthCode',
  8.                 'client_id' => 'XXXXXX',                              
  9.                 'client_secret' => 'XXXXXXXXXXX',  
  10.             )        
  11.         );
  12.  
  13.         $res = recuperer_url($url, $options);
  14.  
  15.         $page = json_decode($res['page'], true);
  16.  
  17.         $token = $page['access_token'];

Télécharger

Compléments à propos du type de flux JWT Bearer

Comme son nom l’indique, ce flux d’autorisation transporte un jeton JWT (JSON Web Token) au lieu d’un simple jeton opaque. Ce faisant, la validation du jeton est plus sûre et des informations sur l’application et l’utilisateur sont directement véhiculées par le jeton.

Le type de flux JWT Bearer réclame plus de paramètres dans la requête et effectue des vérifications concernant l’application cliente et l’utilisateur final.

En plus des paramètres décrits précédemment, JWT Bearer demande que le paramètre suivant lui soit posté :
- assertion : Un jeton JWT.

Par exemple, dans le cas d’un flux d’autorisation (Authorization Code Grant) :

POST /token.oauth2 HTTP/1.1
    Host: oa.dnc.global
    Content-Type: application/x-www-form-urlencoded
    grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer&assertion=eyJhtpZCI6IjE2In0.eyJpc3Mi.J9l-ZhwP.

Dans le cas d’un flux d’authentification d’une application cliente (Client Authentication), le contrôleur Token met en oeuvre l’interface ClientAssertionType. Il faut alors fournir (voir RFC 7523) :
- client_assertion_type : urn:ietf:params:oauth:grant-type:jwt-bearer,
- client_assertion : Un jeton JWT.

Par exemple :

POST /token.oauth2 HTTP/1.1
    Host: as.example.com
    Content-Type: application/x-www-form-urlencoded

    grant_type=authorization_code&
    code=n0esc3NRze7LTCu7iYzS6a5acc3f0ogp4&
    client_assertion_type=urn%3Aietf%3Aparams%3Aoauth%3Aclient-assertion-type%3Ajwt-bearer&client_assertion=eyJhtpZCI6IjE2In0.eyJpc3Mi.J9l-ZhwP.

En cas d’échec, 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 The request method must be POST when revoking an access token La méthode de la demande doit être POST lorsque vous révoquez un jeton d’accès
400 invalid_request Missing parameters : "assertion" required Le parmètre assertion, passant le jeton JWT manque
400 invalid_request JWT is malformed
400 invalid_grant Invalid issuer (iss) provided
400 invalid_grant Invalid subject (sub) provided
400 invalid_grant Expiration (exp) time must be present
400 invalid_grant JWT has expired
400 invalid_grant Expiration (exp) time must be a unix time stamp
400 invalid_grant JWT cannot be used before the Not Before (nbf) time
400 invalid_grant Not Before (nbf) time must be a unix time stamp
400 invalid_grant Invalid audience (aud) Le paramètre audience ( Token Endpoint URI, URI du point de terminaison de jeton) est invalide
400 invalid_grant JSON Token Identifier (jti) has already been used
400 invalid_grant Invalid issuer (iss) or subject (sub) provided La clé publique n’a pas été trouvée pour le couple issuer (iss) - subject (sub) fourni par le jeton
400 invalid_grant JWT failed signature verification

Rafraîchir (actualiser) un jeton d’accès

  (publié initialement le dimanche 20 novembre 2016) par DnC

Le rafraîchissement (ou l’actualisation) d’un jeton d’accès (Access Token Refresh) permet à une application cliente d’en prolonger la validité. La demande est faite au Point d’extrémité de jeton (Token Endpoint) en mentionnant le type de flux "refresh_token".

Les jetons de rafraîchissement ne sont fournis (et le sont alors systématiquement) que lors de la récupération d’un jeton avec les flux Autorisation via un code (Authorization Code Grant) et Autorisation via un mot de passe (User Credentials, Resource Owner Password Credentials Grant).

Avertissement : Une mauvaise utilisation du jeton de rafraîchissement peut faire perdre tout le bénéfice de sécurité d’OpenID Connect. Voir in fine : "Bonnes pratiques pour la sécurité".

Note : cet article s’applique aussi bien à OpenID Connect qu’à OAuth 2.0.

Forme de la demande de rafraîchissement d’un jeton d’accès

La demande ne doit être effectuée que par la méthode POST.
Les paramètres suivants doivent être postés :
- grant_type : "refresh_token".
- refresh_token : le code de rafraîchissement reçu avec le jeton d’accès.

Le client doit s’authentifier selon l’une des deux méthodes suivantes :

1. HTTP Basic Authentication (client_secret_basic)
Le client doit s’identifier en passant ses identifiants dans une entête Authorization.
Exemple avec PHP cUrl :

PHP

  1. // Identification du client avec HTTP Basic Authentication :
  2. $data = array(
  3.     'grant_type'     => 'refresh_token',
  4.     'refresh_token'  => $refresh_token,
  5. );
  6. $h = curl_init($token_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_USERPWD, "{$client_id}:{$client_secret}");
  11. curl_setopt($h, CURLOPT_SSL_VERIFYPEER, false);
  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.  
  15. $res = curl_exec($h);
  16.  
  17. if ( is_array(json_decode($res, true) ) ) {
  18.  
  19.     curl_close($h);
  20.     $res = json_decode($res, true);
  21.  
  22. }

Télécharger

2. Identifiants dans le corps de la demande (client_secret_post)
Les informations d’identification du client doivent être postés :
- client_id : l’ID de l’application cliente.
- client_secret : le secret de l’application cliente.
Les valeurs données pour les paramètres client_id et client_secret doivent correspondre aux valeurs définies lors de l’inscription de l’application cliente sur le serveur.

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
page JSON array access_token : jeton d’accès

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
400 invalid_request Missing parameter : "refresh_token" is required Paramètre manquant : "refresh_token" est attendu
400 invalid_grant Invalid refresh token Jeton de rafraîchissement non valide
400 invalid_grant Refresh token has expired Le jeton de rafraîchissement a expiré

Ainsi que quelques autres réponses identiques à la demande d’un jeton d’accès.

Exemple de code

SPIP

  1. ...
  2. // Rafraîchir le jeton expiré
  3. $options = array(
  4.     'methode' => 'POST',
  5.     'datas' => array(
  6.         'grant_type' => 'refresh_token',
  7.         'refresh_token' => $_SESSION['oauth_refresh_token'],
  8.         'client_id' => 'XXXXX',                          
  9.         'client_secret' => 'XXXXXXXXXXXXXX',  
  10.     ),      
  11. );
  12.  
  13. $url = "http://oa.dnc.global/oauth/token.php";
  14. $refresh_response = recuperer_url($url,$options);
  15. $refresh_status = (int)$refresh_response['status'];
  16.  
  17. if ( $refresh_status == 200 ) {
  18.     $_SESSION['oauth_access_token'] = $refresh_response['access_token'];
  19.     return true;
  20. } else return false;
  21. ...

Télécharger

Attention :
Pour pouvoir rafraîchir un jeton d’accès, l’application cliente doit avoir été inscrite avec le Grant Type "Refresh Token" :

Bonnes pratiques pour la sécurité

Le jeton d’actualisation permet à l’application de demander au serveur d’émettre directement un nouveau jeton d’accès et/ou un nouveau jeton d’identification, sans avoir à authentifier à nouveau l’utilisateur. Cela fonctionnera tant que le jeton d’actualisation n’a pas été révoqué.

On comprendra qu’il convient de n’utiliser ce jeton que dans une relation serveur-serveur (back channel). C’est pourquoi l’obtention d’un jeton d’actualisation n’est possible qu’avec les flux Autorisation avec Code (Authorization Code Grant) ou Autorisation via mot de passe (User Credentials, Resource Owner Password Credentials Grant) [1] .

Dand le cadre d’OpenID Connect, OAuthSD ne fournira le jeton d’actualisation que si la demande de portée (scope) offline_access a été prédéfinie pour l’application cliente, est demandée dans la requête et a été acceptée par l’utilisateur final.

Compte tenu notamment de la nécessité de transmettre le paramètre client_secret, il est indispensable que l’application cliente stocke et transmette ce jeton de façon la plus sécurisée possible. Il convient d’appliquer les bonnes pratiques suivantes au jeton de rafraîchissement [2], sous peine de détruire la sécurité des flux considérés :
- le jeton de rafraîchissement ne doit pas être enregistré dans un cookie ;
- le jeton de rafraîchissement ne doit pas être passé en paramètre d’URL ;
- le jeton de rafraîchissement ne doit pas être envoyé depuis l’user-agent, c’est à dire dans le canal visible (front channel) par exemple à l’aide de code Javascript.
- ... ?

Notre avis sur le rafraîchissement

Avec le mécanisme SLI, OAuthSD permet de tout faire avec le flux le plus sécurisé : Autorisation via un code (Authorization code grant). En particulier, OAuthSD permet de rafraîchir les jetons à l’aide de la ré-authentification silencieuse (Silent Ré-authentication, SRA) qui offre toutes les garanties de sécurité. Une application en est donnée ici : Avertir de la fin de session OIDC et la prolonger.

Notes

[1Dans la liste déroulante, OAuthSD présente la possibilité d’utiliser le jeton de rafraîchisement conjointement à tous les flux ; bien entendu cela ne fonctionnera que pour les "bons" flux.

[2Ainsi qu’aux autres jetons !

Révoquer un jeton

  publié le par DnC

Le sujet est couvert par le document RFC 7009 OAuth 2.0 Token Revocation.
Ce document propose un point d’extrémité supplémentaire pour le serveur d’autorisation OAuth, qui permet aux clients d’avertir le serveur d’autorisation qu’un jeton de rafraîchissement ou d’accès précédemment obtenu n’est plus nécessaire. Cela permet au serveur d’autorisation de nettoyer les données de sécurité. Une demande de révocation invalidera le jeton en question et, le cas échéant, d’autres jetons basés sur la même autorisation.

Point d’extrémité de Révocation

OAuth Server by DnC expose le point de terminaison ’revoke’ pour la révocation d’un jeton d’accès ou de rafraîchissement :

https://oa.dnc.global/oauth/revoke.php

Forme de la demande de Révocation

La demande ne doit être effectuée que par la méthode POST.
Les paramètres suivants doivent être postés :
- token (obligatoire)
- token_type_hint (optionnel) : ’access_token’ ou ’refresh_token’. Peut être manquant ou même erroné, le serveur recherchera le token parmi tous les types possibles.

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

Exemple de requête :

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

    token=45ghiukldjahdnhzdauz&token_type_hint=refresh_token

Réponse

En cas de succès ou si le jeton est invalide, le serveur retourne une réponse HTTP 200.

Remarque : les jetons non valides ne provoquent pas de réponse d’erreur puisque l’objet de la demande de révocation, invalider le jeton désigné, est déjà atteint.

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
400 invalid_request The request method must be POST when revoking an access token La méthode de la demande doit être POST lorsque vous révoquez un jeton d’accès
400 invalid_request Token type hint must be either "access_token" or "refresh_token" Le type de jeton doit être "access_token" ou "refresh_token"
400 invalid_request Missing token parameter to revoke Il manque le type du jeton à révoquer

Validation du jeton d’accès par interrogation du point d’extrémité "resource"

  publié le par DnC

La norme OAuth 2.0 n’indique pas comment les serveurs de données protégées qui reçoivent un jeton d’accès doivent procéder pour le valider, que ce soit localement ou en s’adressant à point d’entrée dédié à sa validation [1] [2].

Avec OAuthSD, les applications peuvent accéder au point d’extrémité "resource " pour valider le jeton d’accès reçu. Notons qu’OAuthSD implémente également le standard OpenID Connect "Introspection Endpoint".

Les données de profil de l’utilisateur final sont transmises avec la réponse, ce qui renforce la sécurité des données protégées : en permettant l’authentification de l’utilisateur final au niveau des fournisseurs de ressources protégées, on leur permet d’adapter la réponse en fonction de son statut.

Le fait que OAuthSD propose cette méthode d’authentification ne change en rien au fait que ce serveur est entièrement compatible avec la norme OAuth 2.0 - OpenID Connect.

Rappelons le fait que, très souvent (le plus souvent ?) le serveur d’autorisation et les API protégées appartiennent à la même organisation. Dans cette configuration, la façon dont le jeton d’accès est généré et vérifié est opaque pour l’utilisateur. Le cas qui nous intéresse est celui dans lequel il y a une certaine indépendance des API et du serveur d’autorisation.

Cette problématique est exposée ici dans sa généralité : Validation du jeton par une ressource protégée.

Demande de validation du jeton d’accès

Une solution est de demander au serveur d’autorisation d’authentifier le jeton d’accès. La demande est adressée par le serveur de ressource à OAuthSD sur le point d’extrémité de ressource à l’URI :

https://oa.dnc.global/oauth/resource.php

Le jeton est passé par l’une des méthodes suivantes : Auth Header, GET ou POST.

Une seule méthode doit être utilisée à la fois, sinon le serveur retourne ’400, invalid_request’.

Méthode Auth Header :
C’est la méthode recommandée.

Méthodes POST :
Le jeton est passé avec le paramètre "access_token". Lorsque vous placez le jeton dans le corps de la requête HTTP, la méthode doit être POST ou PUT. Sinon, le serveur retourne 400, ’invalid_request’, ’When putting the token in the body, the method must be POST or PUT’, ’#section-2.2’.

Le type de contenu pour les requêtes POST doit être "application / x-www-form-urlencoded. Si ce n’est pas le cas, le serveur retourne 400, ’invalid_request’, ’The content type for POST requests must be "application/x-www-form-urlencoded".

Méthodes GET :
Cette méthode n’est pas recommandée pour des raisons de sécurité.

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 sur le jeton d’accès :

index type valeur
status entier code HTTP
headers string Headers de la réponse
page string JSON Array :
success : true,
client_id : ID de l’application cliente
user_id : ID de l’utilisateur final
expires : Unix Time de la fin de validité (long) (secondes depuis le 1° janvier 1970).
scope : liste des scopes de l’application cliente

 

En cas d’échec, l’entête de la réponse contient un champ ’WWW-Authenticate’ avec le type d’authentification suivi de l’erreur.

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 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 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
403 insufficient_scope The request requires higher privileges than provided by the access token Le scope (éventuellement) transmis avec la requête ne correspond pas aux scopes autorisés pour l’application.

Notes :
- Si on adresse une requête avec un jeton d’accès nul (empty string), le contrôleur ressource retourne le code HTTP 401, mais le corps de la réponse est vide, alors que l’on s’attendrait à error = ’invalid_token’. Il s’ensuit que l’on devrait traiter tous les codes HTTP 401 dont l’erreur n’est pas ’expired_token’ comme étant une tentative de violation d’accès.
- En cas de tentative de violation d’accès (code HTTP 401 et error = ’invalid_request’, le mieux est sans doute de ne rien répondre pour ne pas renseigner l’attaquant.

Exemples

Demande de validation d’un token d’accès :

SPIP

  1. /*
  2. Autorisation avec OAuth Server by DnC
  3. Auteur : Bertrand degoy
  4. Copyright (c) 2016 DnC
  5. */
  6.  
  7. function oauth_authorize($accesstoken) {
  8.  
  9.     $Ok = false;
  10.  
  11.     if ( !empty( $accesstoken ) ) {
  12.  
  13.         // Interroger OAuth Server by DnC
  14.         include_spip('inc/distant');
  15.         $url = "http://oa.dnc.global/oauth/resource.php?access_token=" . $accesstoken;
  16.         $resource_response = recuperer_url($url);  
  17.        
  18.         if ( (int)$resource_response['status'] === 200 ) {
  19.             $page = json_decode($resource_response['page'], true);
  20.             if ( $page['success'] == 'true' ) {
  21.                 if ( isset($_SERVER["HTTP_ORIGIN"]) ) {
  22.                     // Accès HTTP (CORS) : autoriser l'origine
  23.                     include_spip('inc/headers');
  24.                     $issuer = trim(strtr($_SERVER["HTTP_ORIGIN"], '<>"\'', '[]##'));    
  25.                     header('Access-Control-Allow-Origin', $issuer);
  26.                 }
  27.                 $Ok = true;
  28.             }
  29.         }
  30.  
  31.     }
  32.  
  33.     return $Ok;
  34.  
  35. }

Télécharger

L’interrogation du serveur d’autorisation à chaque accès d’une ressource protégée peut le surcharger. Pour éviter cela, il est possible de 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 (quelques minutes par exemple), l’essentiel étant de ne pas bombarder le serveur. Voici un exemple :

SPIP

  1. $res = recuperer_url_cache( $url, array('delai_cache' => 300) );

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 ( $accesstoken = $_GET['token'] ) {
  7.             // Vérifier le jeton d'accès
  8.             return oauth_authorize($accesstoken);    
  9.         } else return false;
  10.     }
  11. }

Télécharger

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

Introspection Endpoint

Le document RFC 7662 : OAuth 2.0 Token Introspection propose une méthode identique dans le principe et très proche dans la réalisation. Les données retournées sont au format JSON et se rapprochent du contenu d’un JSON Web Token (JWT).

OAuthSD offre donc également le point d’extrémité introspection suivant :

https://oa.dnc.global/oauth/introspect.php

La demande est effectuée comme pour l’appel à ’resource’, mais le paramètre est ’token’ au lieu de ’access_token’.

En cas de succès, la réponse est conforme au document précité. Les données retournées sont ’active’, ’scope’, ’client_id’, ’username’, ’exp’.

A ces données, s’ajoute ’sub’ dont la valeur est l’E-mail de l’utilisateur. Toutefois, cette donnée n’est présente que si l’E-mail de l’utilisateur a été vérifié (champ ’verify’ de la table Users).

En cas d’erreur, la réponse est identique à celle de ’resource’.

Nota : voir également : Emettre un jeton d’accès en tant que JWT.

Notes

[1La spécification OAuth 2.0, RFC6749, aborde très précisément ce problème dans la section 7 : Les méthodes utilisées par le serveur de ressource pour valider le jeton d’accès (ainsi que toute réponse d’erreur) dépassent le cadre de cette spécification, mais impliquent généralement une interaction ou une coordination entre le serveur de ressources et le serveur d’autorisation".

Validation du jeton d’accès avec la déclaration at_hash du jeton d’identité

  publié le par DnC

Si le jeton d’ID contient une déclaration at_hash, le client PEUT l’utiliser pour valider le jeton d’accès.

Ceci est une précaution de sécurité utile (voire indispensable ?). En effet, nous disposons de la faculté de vérifier le JWT avec sa signature, mais nous ne disposons pas de la faculté de valider un jeton d’accès de façon autonome (puisque c’est un jeton opaque) [1]

Encore un bon point pour OpenID Connect ! Un bon point aussi pour la bibliothèque oauth2-server-php de Brent Schaffer qui insère la déclaration at_hash dans le jeton d’identité. Donc un bon point aussi pour OAuthSD.

Ce qui suit est une traduction d’extraits de OpenID Connect Core 1.0

3.1.3.8. Validation du jeton d’accès

Lors de l’utilisation du flux de code d’autorisation, si le jeton d’ID contient une déclaration at_hash, le client PEUT l’utiliser pour valider le jeton d’accès de la même manière que pour le flux implicite, tel que défini dans la section 3.2.2.9, Jeton d’accès renvoyé à partir du point d’extrémité Token.

3.2.2.9. Validation du jeton d’accès

Pour valider un jeton d’accès émis à partir du point d’extrémité d’autorisation avec un jeton d’ID, le client DOIT faire ce qui suit :

- Hash les octets de la représentation ASCII de l’access_token avec l’algorithme de hachage spécifié dans JWA pour le paramètre Alg Header de l’en-tête JOSE de Token ID. Par exemple, si l’alg est RS256, l’algorithme de hachage utilisé est SHA-256.
- Prendre la moitié la plus à gauche du hachage et base64url encoder.
- La valeur de at_hash dans le jeton d’identification DOIT correspondre à la valeur produite à l’étape précédente.

Exemple de code :
PHP

  1. if ( @$jwt['at_hash'] ) {
  2.     // Verify access token
  3.     $hash = hash('sha256', $access_token);
  4.     $ath = substr($hash, 0, strlen($hash) / 2);
  5.     $encryptionUtil = new \OAuth2\Encryption\Jwt();
  6.     $ath = $encryptionUtil->urlSafeB64Encode($ath);
  7.     if ( $ath !== $jwt['at_hash'] ) {
  8.         // Token request error
  9.         exit ('Access Token not valid');
  10.     }      
  11. }

Télécharger

Note :
- Dans l’état actuel du développement, le flux Authorization Code d’OpenID Connect ne comporte la déclaration at_hash que dans le cadre du flux Implicit (type de réponse ’idtoken token’). La généralisation de at_hash est à l’étude. Voyez : https://github.com/bshaffer/oauth2-server-php-docs/issues/113.

Notes

[1Un argument souvent évoqué pour justifier la nécessité de la vérification est que le jeton d’accès et le jeton d’identité ne sont pas couplés strictement.

Le jeton d’accès peut être un JWT signé

  publié le par DnC

La spécification OAuth 2.0 est extrêmement vague en ce qui concerne la forme que devrait avoir un jeton d’accès. Voyez la discussion ici.

Le jeton d’accès peut être un JWT signé (ou comment ré-inventer OpenID Connect)

La valeur true pour la constante de configuration USE_JWT_ACCESS_TOKENS provoque l’émission du jeton d’accès sous la forme d’un JWT signé.

Voici ce qu’en dit Brent Shaffer :

"vous pouvez utiliser les jetons d’accès JWT si vous avez des systèmes distribués et que vous devez valider l’authenticité d’un jeton auprès de plusieurs parties sans avoir à passer un appel réseau.
Par exemple, un jeton est attribué par le serveur d’autorisations. Ce jeton est un jeton Web JSON signé par la clé privée du serveur d’autorisations. Les serveurs de ressources (vers lesquels les appels d’API sont effectués) sont répartis dans le monde entier et exécutent plusieurs applications. Tant que les serveurs de ressources disposent de la clé publique du serveur d’autorisations, qui n’a pas besoin d’être sécurisée, ils peuvent valider les jetons rapidement sans aucun appel réseau. Les jetons n’ont même pas besoin d’être persistés."

Dans la mesure où nous avons OpenID Connect et le jeton d’identité au format JWT, cette faculté ne parait pas essentielle. Les deux méthodes présentent l’inconvénient de ne pas permettre de vérifier l’actualité de la session OIDC.