i-TegoDocumentationAuthentification > OpenID Connect OAuth Serveur dédié > Développer > OAuth 2.0 > Autorisation de serveur à serveur (Client Credentials Grant)

OAuth 2.0 : Autorisation de serveur à serveur, exemple complet

  publié le par i-Tego WM

Le flux Autorisation de Serveur à serveur (Client Credentials Grant) du protocole OAuth 2.0 permet à une application cliente d’obtenir un jeton d’accès au vu de ses identifiants.

Du côté du client, utiliser l’authentification OAuth pour interroger une API HTTP REST peut se réduire à une simple fonction.

Nous indiquons également comment les choses se passent du côté du serveur de ressource.

Le diagramme ci-contre donne une idée simplifiée de la suite des échanges entre les 3 pôles d’un flux de type Serveur à serveur.
Cliquez sur l’image pour l’agrandir


Ce flux n’identifie pas l’utilisateur final, qui n’entre pas en ligne de compte. Il est donc utile pour effectuer des tâches requérant un accès de l’application à des ressources protégées hors du contrôle de l’utilisateur final.

Du côté du client

Voici un exemple en PHP pour SPIP 3.1 :

SPIP

  1. /**
  2. * Interface avec OAuthSD, Flux d'autorisation de serveur à serveur
  3. * (Client Credentials Grant).
  4. * Cette fonction interroge une API protégée et retourne le résultat au format JSON.
  5. * Si l'API refuse l'accès, une nouvelle demande de jeton est lancée automatiquement.  
  6. *
  7. * @param mixed $request : URI de l'appel à l'API REST
  8. * @return mixed string, bool : Résultat de la requête au format JSON ou false en
  9. * cas de refus d'accès.
  10. *
  11. * Auteur: B.Degoy DnC https://degoy.com/#dnc_conteneur_contact
  12. * licence GNU/GPL v3
  13. *
  14. */
  15. function ProtectedApi_GET_auto( $request ) {
  16.  
  17.  
  18.     $session_name = session_name();
  19.     if ( empty($session_name) ) {
  20.         session_name('PHPSESSID');
  21.     }
  22.     session_start();
  23.  
  24.     include_spip('inc/headers');
  25.     include_spip('inc/distant');
  26.  
  27.     if ( isset($_GET['token']) ) $token = $_GET['token'];
  28.     if ( !$token  ) $token = $_SESSION['oauth_access_token'];    
  29.  
  30.     if ( !empty($token) ) {
  31.  
  32.         // Interroger la ressource protégée
  33.  
  34.         $request_response = recuperer_url($request . "&token=$token");
  35.  
  36.         if ( (int)$request_response['status'] == 200 ) {
  37.             // Ok
  38.             return $request_response['page'];
  39.  
  40.         } elseif ( (int)$request_response['status'] == 401
  41.                        OR (int)$request_response['status'] == 403 ) {
  42.  
  43.             // Pas le droit d'accéder à cette ressource.
  44.             // Commencer par vérifier le jeton
  45.             $url = "http://oa.dnc.global/oauth/resource.php?access_token=" . $token;
  46.             $resource_response = recuperer_url($url);
  47.  
  48.             $status = (int)$resource_response['status'];
  49.             $page = json_decode($resource_response['page'], true);
  50.             $error = $page['error'];
  51.             $error_description = $page['error_description'];  
  52.  
  53.             switch( $status ) {
  54.  
  55.                 case 200 :
  56.                     // Si le jeton était valide, ne pas boucler!
  57.                     return false;
  58.                     break;
  59.  
  60.                 default :
  61.  
  62.                     // Demander un nouveau jeton et recommencer
  63.                     unset($_SESSION['oauth_access_token']);
  64.                     return _replay( $request );
  65.                     break;
  66.  
  67.             }
  68.             return false;
  69.         }
  70.     } else {
  71.  
  72.         // Demander un nouveau jeton et recommencer
  73.         return _replay( $request );
  74.  
  75.     }
  76.  
  77. }
  78. function _replay( $request ) {
  79.     if ( $token = GetToken() ) {  
  80.         $_SESSION['oauth_access_token'] = $token;
  81.         $_GET['token'] = $token;
  82.         ProtectedApi_GET_auto( $request );
  83.     } else return false;
  84. }
  85.  
  86.  
  87. /**
  88. * Demander un nouveau jeton d'accès au serveur OAuth
  89. * Flux Client Credentials Grant
  90. *
  91. * Auteur: B.Degoy DnC https://degoy.com/#dnc_conteneur_contact
  92. * licence GNU/GPL v3
  93. *
  94. */
  95. function GetToken() {
  96.  
  97.     // Interroger Token
  98.     $jeton = '';
  99.     $url = 'http://oa.dnc.global/token.php';
  100.     $options = array(
  101.         'method' => 'POST',
  102.         'datas' =>  array(
  103.             'grant_type' => 'client_credentials',
  104.             'client_id' => 'radar',                              
  105.             'client_secret' => '01fc4587ab1c23ff456e448dab18327a',  
  106.         )        
  107.     );
  108.  
  109.     $res = recuperer_url( $url, $options );
  110.  
  111.     $page = json_decode($res['page'], true);
  112.  
  113.     $token = $page['access_token'];
  114.  
  115.     return ( (!empty($token))? $token : false );    
  116.  
  117. }

Télécharger

 

L’utilisation de la fonction est très simple.
La fonction retourne un objet JSON représentant la réponse, que l’on peut facilement transformer en Array PHP :

PHP

  1. // Le nom de l'application cliente
  2. define ( 'APPNAME', 'radar' );
  3. // La requete
  4. $req = "http://chemindeleau.com/?page=gis.json&pt=1";
  5.  
  6. echo '<h3>Résultat de : ' .  $req . '</h3><br/>';
  7.  
  8. $api_result = ProtectedApi_GET_auto( $req );
  9. if ( $api_result == false ) {
  10.     $api_result = "Erreur !";
  11. } else {
  12.     echo '<pre>';
  13.     print_r( json_decode( $api_result, true ));
  14.     echo '</pre>';    
  15. }

Télécharger

Du côté du serveur de ressource

Voici un exemple de serveur de ressource simple :

SPIP

  1. #CACHE{2}
  2.  
  3. [(#REM)
  4. Liste de Tagazd en JSON.
  5. Nécessite les plugins geobase et gis.  
  6.  
  7. Exemple d'appel:
  8. http://xxx.com/?page=gis.json&pt=nn
  9. où nn est l'id du point dans la table gis.
  10.  
  11. Copyright (c) 2001-2016
  12. Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James  
  13. Copyright(c) 2013-2016 DnC
  14. Auteur: B.Degoy DnC https://degoy.com/#dnc_conteneur_contact
  15. Licence GNU/GPL v3
  16.  
  17. ]
  18. <?php
  19.  
  20. function oauth_authorize($accesstoken) {
  21.     if ( !empty( $accesstoken ) ) {
  22.         // Interroger OAuth Server by DnC
  23.         include_spip('inc/distant');
  24.         // Vérifier le jeton d'accès (access token)
  25.         $url = "https://oa.dnc.global/oauth/resource.php?access_token=" . $accesstoken;
  26.         $res = recuperer_url($url);  
  27.         if ( (int)$res['status'] === 200 ) {
  28.             return true ;
  29.         }
  30.     }
  31.     return false;
  32. }
  33.    
  34. if ( !(bool)@oauth_authorize(_request('token')) ) {
  35.     header("HTTP/1.0 401 Unauthorized");
  36.     die();  
  37. }
  38.  
  39. // Si le jeton est valide, demander à SPIP d'émettre le contenu au format JSON :
  40.  
  41. ?>
  42. #HTTP_HEADER{Content-Type: application/json; charset=#CHARSET}
  43. [<BOUCLE_docp(DOCUMENTS){gis}{', '}{distancefrom #ENV{pt}, <=, 100}{extension IN png,jpg,gif,html}{par distance}{0,#ENV{limit,100}}>
  44.     [(#ARRAY{
  45.         id,#ID_GIS,
  46.         titre,#TITRE,
  47.         distance,[(#DISTANCE|format_kmm)],
  48.         quadrant,[(#ENV{pt,1}|quadrant{#ID_GIS})],
  49.         lat,#LAT,
  50.         lon,#LON,
  51.         url,#URL_SITE_SPIP/?page=photo&id_document=#ID_DOCUMENT,
  52.     }|json_encode)]
  53. </BOUCLE_docp>]

Télécharger

La première partie du code en PHP valide le jeton d’accès auprès du serveur OAuthSD. En cas d’échec, l’exécution est interrompue et le serveur retourne le code HTTP 401 (Non autorisé) et un contenu vide.

Dans la deuxième partie "écrite en SPIP", vous remarquerez comme SPIP permet de générer facilement toute sortes de contenus à partir de données diverses. Nous avons ici une jointure entre des documents et des points géographiques retournée au format application/json.

Lancer un test

Ce test met en œuvre le code présenté ci-dessus.
Les rôles sont les suivants :
- application cliente et utilisateur final : radar (r.dnc.global),
- serveur de ressource (données protégées) (API Rest) : chemindeleau.com,
- serveur d’authentification : oa.dnc.global,

Lancer le test get-gis-data_exemple

Lors de la demande de vos login et mot de passe, si vous ne voulez pas vous inscrire sur ce serveur en tant qu’utilisateur, vous pourrez utiliser les identifiants suivants :
E-mail or pseudo : bebert
Password : 012345678

L’application cliente demande au serveur les points situés à moins de 100km autour du point pt=1, qui correspond à la "Fontaine chaude" de Bourbonnes-les-bains. Le résultat est ordonné par distance croissante.

Vous obtiendrez la réponse suivante :