Accueil > OpenID Connect OAuth Server par DnC > Développer > Validation du jeton d’accès par interrogation du serveur d’autorisation (...)

Validation du jeton d’accès par interrogation du serveur d’autorisation (Introspection)

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 OAuth Server by DnC, les applications peuvent accéder au point d’extrémité "resource " pour valider le jeton d’accès reçu. Le standard "Introspection Endpoint" est également implémenté.

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 OAuth Server by DnC 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 d’accès 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 à OAuth Server by DnC sur le point d’extrémité de ressource à l’URI :

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

Le jeton est passé avec le paramètre "access_token" 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 :
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 (remarquez la présence des données relatives à l’utilisateur final) :

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
id_user : ID interne de l’utilisateur final
username : ID OAuth de l’utilisateur final
given_name : prénom
middle_name : complément de nom
family_name : nom
nickname : surnom
profile : URL de la page de profil
picture : URL de la photo
website : URL du site web
email : adresse E-mail

verified : true si l’email a été vérifié
gender : male/female
birthday : date de naissance

zoneinfo : décalage horaire /UTC
locale : code langue selon RFC-3066
phone_number : numéro de téléphone
street_address, locality, region, postal_code, country : adresse
updated_time : instant de mise à jour du profil
created_time : instant de création du profil
statut : statut de l’utilisateur
maj : instant de mise à jour des données

Notes :
- Les 5 premières variables correspondent au standard OAuth. Le fait qu’il y en ait plus ne change rien au fait que OAuth Server by DnC répond parfaitement au standard.
- user_id et username ont la même valeur et correspondent à l’identifiant unique de l’utilisateur final. Le fait qu’il y ait deux variables de valeurs identiques provient d’une ambiguïté entre le standard et OAuth 2.0 Server PHP.
- les données en italiques sont transmises sous certaines conditions, en fonction de la configuration d’une application particulière.

 

En cas d’échec, 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 recommandé 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).

OAuth Server by DnC offre donc également le point d’extrémité 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".