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
- /*
- Autorisation avec OAuth Server by DnC
- Auteur : Bertrand degoy
- Copyright (c) 2016 DnC
- */
- function oauth_authorize($accesstoken) {
- $Ok = false;
- // Interroger OAuth Server by DnC
- include_spip('inc/distant');
- $url = "http://oa.dnc.global/oauth/resource.php?access_token=" . $accesstoken;
- $resource_response = recuperer_url($url);
- if ( (int)$resource_response['status'] === 200 ) {
- if ( $page['success'] == 'true' ) {
- // Accès HTTP (CORS) : autoriser l'origine
- include_spip('inc/headers');
- }
- $Ok = true;
- }
- }
- }
- return $Ok;
- }
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
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
- function _autoriser_gis($faire, $quoi, $id, $qui, $options) {
- if ( $qui['statut'] == '0minirezo' ) {
- // Toujours autoriser un administrateur
- return true;
- } else {
- if ( $accesstoken = $_GET['token'] ) {
- // Vérifier le jeton d'accès
- return oauth_authorize($accesstoken);
- } else return false;
- }
- }
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.