OAuth 2.0 : Autorisation de serveur à serveur, exemple complet

, par DnC

Nous sommes dans le cas du flux Autorisation de Serveur à serveur (Client Credentials Grant) du protocole OAuth 2.0.

Du côté d’une application cliente, 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.

PNG - 82.3 ko

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


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.  
  23. include_spip('inc/headers');
  24. include_spip('inc/distant');
  25.  
  26. if ( isset($_GET['token']) ) $token = $_GET['token'];
  27. if ( !$token ) $token = $_SESSION['oauth_access_token'];
  28.  
  29. if ( !empty($token) ) {
  30.  
  31. // Interroger la ressource protégée
  32.  
  33. $request_response = recuperer_url($request . "&token=$token");
  34.  
  35. if ( (int)$request_response['status'] == 200 ) {
  36. // Ok
  37. return $request_response['page'];
  38.  
  39. } elseif ( (int)$request_response['status'] == 401
  40. OR (int)$request_response['status'] == 403 ) {
  41.  
  42. // Pas le droit d'accéder à cette ressource.
  43. // Commencer par vérifier le jeton
  44. $url = "http://oa.dnc.global/oauth/resource.php?access_token=" . $token;
  45. $resource_response = recuperer_url($url);
  46.  
  47. $status = (int)$resource_response['status'];
  48. $page = json_decode($resource_response['page'], true);
  49. $error = $page['error'];
  50. $error_description = $page['error_description'];
  51.  
  52. switch( $status ) {
  53.  
  54. case 200 :
  55. // Si le jeton était valide, ne pas boucler!
  56. return false;
  57. break;
  58.  
  59. default :
  60.  
  61. // Demander un nouveau jeton et recommencer
  62. unset($_SESSION['oauth_access_token']);
  63. return _replay( $request );
  64. break;
  65.  
  66. }
  67. return false;
  68. }
  69. } else {
  70.  
  71. // Demander un nouveau jeton et recommencer
  72. return _replay( $request );
  73.  
  74. }
  75.  
  76. }
  77. function _replay( $request ) {
  78. if ( $token = GetToken() ) {
  79. $_SESSION['oauth_access_token'] = $token;
  80. $_GET['token'] = $token;
  81. ProtectedApi_GET_auto( $request );
  82. } else return false;
  83. }
  84.  
  85.  
  86. /**
  87. * Demander un nouveau jeton d'accès au serveur OAuth
  88. * Flux Client Credentials Grant
  89. *
  90. * Auteur: B.Degoy DnC https://degoy.com/#dnc_conteneur_contact
  91. * licence GNU/GPL v3
  92. *
  93. */
  94. function GetToken() {
  95.  
  96. // Interroger Token
  97. $jeton = '';
  98. $url = 'http://oa.dnc.global/oauth/token.php';
  99. $options = array(
  100. 'method' => 'POST',
  101. 'datas' => array(
  102. 'grant_type' => 'client_credentials',
  103. 'client_id' => 'radar',
  104. 'client_secret' => '01fc4587ab1c23ff456e448dab18327a',
  105. )
  106. );
  107.  
  108. $res = recuperer_url( $url, $options );
  109.  
  110. $page = json_decode($res['page'], true);
  111.  
  112. $token = $page['access_token'];
  113.  
  114. return ( (!empty($token))? $token : false );
  115.  
  116. }

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 = "http://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. ?>
  40. #HTTP_HEADER{Content-Type: application/json; charset=#CHARSET}
  41. [<BOUCLE_docp(DOCUMENTS){gis}{', '}{distancefrom #ENV{pt}, <=, 100}{extension IN png,jpg,gif,html}{par distance}{0,#ENV{limit,100}}>
  42. [(#ARRAY{
  43. id,#ID_GIS,
  44. titre,#TITRE,
  45. distance,[(#DISTANCE|format_kmm)],
  46. quadrant,[(#ENV{pt,1}|quadrant{#ID_GIS})],
  47. lat,#LAT,
  48. lon,#LON,
  49. url,#URL_SITE_SPIP/?page=photo&id_document=#ID_DOCUMENT,
  50. }|json_encode)]
  51. </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, 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 données protégées : chemindeleau.com,
- serveur d’authentification : oa.dnc.global,

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 :

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