Accueil > OpenID Connect OAuth Server par DnC > Développer > OpenID Connect > OpenID Connect : Exemples complets du flux d’Autorisation via un code puis (...)

OpenID Connect : Exemples complets du flux d’Autorisation via un code puis requête UserInfo

Les scripts testopenid testent le flux d’Autorisation via un code (Authorization Code Flow), et enchaînent sur une demande UserInfo, en ne laissant passer aucune erreur : tous les cas non conformes répertoriés dans cette documentation donneront lieu à l’affichage d’un message d’erreur.

Les deux méthodes de validation du JWT sont illustrées : Validation locale, Introspection.

Ces scripts ne sont pas conçu pour une application en production. Cependant, ils fournissent un flux complet que vous pourrez adapter au besoin de votre application cliente. En particulier, il serait opportun d’utiliser les fonctions d’affichage et de gestion des erreurs du système de l’application cliente.

Notez que ces scripts jouent le rôle de Point d’extrémité de redirection, ou URI de retour à l’application cliente (Redirection Endpoint). Voyez : OAuth 2.0 : Points d’extrémité.

 

Première version : Validation locale du JWT

La vérification du jeton JWT est effectuée localement par la fonction decode_jwt(), qui est décrite dans cet article : JSON Web Token (JWT).

PHP

  1. <?php
  2. /*
  3. testopenid3.php
  4.  
  5. Verbose Test of OpenID Connect Authorization Code Flow
  6.  
  7. This code is meant for testing, not for production !
  8.  
  9. At the authentification step, if you don't want to create an account
  10. on OAuthSD, use this credentials :
  11. login = bebert
  12. password = 012345678
  13.  
  14. Author :
  15. Bertrand Degoy https://degoy.com
  16. Credits :
  17. bschaffer https://github.com/bshaffer/oauth2-server-php
  18.  
  19. Licence : MIT licence
  20. Copyright (c) 2016 - DnC
  21.  
  22. */
  23.  
  24. //***** Configuration *****
  25.  
  26. /* Client's Public Key
  27. Warning : no double-quote, no indent!
  28. */
  29. define ('PUBLIC_KEY',
  30. '-----BEGIN PUBLIC KEY-----
  31. MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA2dCHybdu/1pDk5BHSxMA
  32. nMGygm8lm6s2hnru3GPH2JyMxrd92dC7TljI21P+egsnmjsUzaS1IWZPvIFvEOwO
  33. 5wP8gFyNm8fFkXSmrAEHXLFJ6WufF+f5Fg3pU5GDPjT5Z7ccWab5NM9w7ec433J1
  34. XtePh2QjUbibu4rpwYh8ADODAJkyIRMhhYXqK0n8GgojkcgEZ172sB/NdbcNALPy
  35. Qg0lMd3/AKxavTSSm9LslEyP+ZwBvTENzhoeQV2V7ZQ5xIZs6VBgrnsnYbgfbdQ+
  36. Dbk2FRbtB2+g4rKbN04JheaZyFORseoigVJ6asQ5lUS/3cMIUj2C+VBj4xAyXp00
  37. TMH8GtiGIQkVYjBd/Lsza11YwBOA8YYvDnTs/kzy9CqHjETdIHUlNqeaFbHSYTST
  38. rUl9QxBN+JAVGs9YY9MWoiVsGex4MsTwf3PanKIlavKXeFSwppwMMvmdt+yrGraH
  39. UKv5QP5NMbOu6/BghbuQZP4MoUnRxQxt8PN2e5M2b358C3tctgRQhRGBWaYw8B5J
  40. /drz5VA8s14NkG162lBW7PLYhLqm8u2hpqIlOCVndwW2W+bCkXrfjj3jBHe4yauQ
  41. vyQWcv3KaBV2HsUoY2sCAaC5nB46SV0UkAycX8xyqOsGJA64m2S+ntOQkB9R2x2y
  42. 4DjfJHTRTe2uXsaiYFahoLECAwEAAQ==
  43. -----END PUBLIC KEY-----
  44. ');
  45.  
  46. $client_id = 'testopenid';
  47. $client_secret = 'thesecret';
  48.  
  49. $authorization_endpoint = 'https://oa.dnc.global/authorize';
  50. $token_endpoint = 'https://oa.dnc.global/token';
  51. $userinfo_endpoint = 'https://oa.dnc.global/userinfo';
  52.  
  53. //*** End of configuration ***
  54.  
  55. ini_set('display_errors', 1);
  56.  
  57. // Set session
  58. session_save_path('/home/oadnc/sessions_oauthsd');          
  59. session_name('oauthsd');
  60.  
  61.  
  62. if ( !isset($_GET['error']) ) {
  63.  
  64.     if ( isset($_GET['code']) ) {
  65.  
  66.         if ( isset($_GET['state']) ) {
  67.  
  68.             // Check state
  69.             if ( $_GET['state'] == $_SESSION['state'] ) {
  70.            
  71.                 // Step 2. Token request
  72.  
  73.                 $code = $_GET['code'];
  74.  
  75.                 $data = array(
  76.                     'grant_type' => 'authorization_code',
  77.                     'code' => $code,
  78.                 );
  79.                 $h = curl_init($token_endpoint);
  80.                 curl_setopt($h, CURLOPT_RETURNTRANSFER, true);
  81.                 curl_setopt($h, CURLOPT_TIMEOUT, 10);
  82.                 curl_setopt($h, CURLOPT_USERPWD, "{$client_id}:{$client_secret}");
  83.                 curl_setopt($h, CURLOPT_POST, true);
  84.                 curl_setopt($h, CURLOPT_HTTPHEADER,
  85.                             array('Content-Type: application/x-www-form-urlencoded'));
  86.                 curl_setopt($h, CURLOPT_POSTFIELDS, http_build_query($data));
  87.                 //curl_setopt($h, CURLOPT_SSL_VERIFYPEER, false);
  88.  
  89.                 $res = curl_exec($h);
  90.  
  91.                 if ($res)  {
  92.  
  93.                     curl_close($h);
  94.                     $res = json_decode($res, true);
  95.  
  96.                     if  ( empty($res['error'] ) ) {
  97.  
  98.                         // Validate signed JWT token using client's public key
  99.                         if ( $payload = decode_jwt($res['id_token'],
  100.                                                         PUBLIC_KEY, 'RS256') ) {
  101.  
  102.                             // If Token Response is valid goto step 3
  103.                             // Step 3. Get UserInfo
  104.                             $access_token = $res['access_token'];
  105.  
  106.                             /* Auth Header Methode
  107.                             $headr = array();
  108.                             $headr[] = 'Authorization: Bearer ' . $access_token;
  109.                             $h = curl_init();
  110.                             curl_setopt($h, CURLOPT_URL, $userinfo_endpoint);
  111.                             curl_setopt($h, CURLOPT_RETURNTRANSFER, true);
  112.                             curl_setopt($h, CURLOPT_TIMEOUT, 10);
  113.                             curl_setopt($h, CURLOPT_HTTPHEADER, $headr);
  114.                             //curl_setopt($h, CURLOPT_SSL_VERIFYPEER, false);
  115.                             //*/
  116.  
  117.                             //* Post Methode  
  118.                             $data2 = array(
  119.                                 'access_token' => $access_token,
  120.                                 'state' => $state,
  121.                             );
  122.                             $h = curl_init($userinfo_endpoint);
  123.                             curl_setopt($h, CURLOPT_RETURNTRANSFER, true);
  124.                             curl_setopt($h, CURLOPT_TIMEOUT, 10);
  125.                             curl_setopt($h, CURLOPT_POST, true);
  126.                             curl_setopt($h, CURLOPT_HTTPHEADER,
  127.                                    array('Content-Type:
  128.                                    application/x-www-form-urlencoded'));  
  129.                             curl_setopt($h, CURLOPT_POSTFIELDS,
  130.                                   http_build_query($data2));
  131.                             //*/
  132.  
  133.                             $res = curl_exec($h);
  134.  
  135.                             if ( $res ) {
  136.  
  137.                                 curl_close($h);
  138.                                 $res = json_decode($res, true);
  139.  
  140.                                 if  ( empty($res['error'] ) ) {
  141.  
  142.                                     // Check User ID
  143.                                     if ( $payload['sub'] == $res['sub'] ) {
  144.  
  145.                                         // Everithing Ok !
  146.                                         echo "UserInfo Response:\n";
  147.                                         print_r($res);
  148.  
  149.                                     } else  
  150.                                         // User of ID Token doesn't match UserInfo's one
  151.                                         exit('User mismatch, got : ' . $res['sub']);
  152.  
  153.                                 } else
  154.                                     // Token request error
  155.                                     exit ('UserInfo Request error : ' . $res['error'] . ' : '
  156.                                                      . $res['error_description']);
  157.  
  158.                             } else {
  159.                                 // Curl error during UserInfo request
  160.                                 exit('UserInfo Request error : ' . curl_error($h));
  161.                                 curl_close($h);
  162.                             }
  163.  
  164.                         } else
  165.                             // Invalid id_token
  166.                             exit('Error : Invalid ID Token');
  167.  
  168.                     } else {
  169.                         // Token request error
  170.                         exit ('Token request error : ' . $res['error'] . ' : '
  171.                                               . $res['error_description']);
  172.                     }
  173.  
  174.                 } else {
  175.                     // Curl error during Token request
  176.                     exit('Token Request error : ' . curl_error($h));
  177.                     curl_close($h);
  178.                 }
  179.            
  180.             } else
  181.                 // Wrong State
  182.                 exit("Authorization error : incoherent State");
  183.  
  184.         } else
  185.             // Missing State
  186.             exit("Authorization error : missing State");
  187.            
  188.     } else {
  189.  
  190.         // Step 1. Authorization Code request
  191.  
  192.         @session_regenerate_id();
  193.         $state = session_id();
  194.         $_SESSION['state'] = $state;
  195.        
  196.         $data = array(
  197.             'response_type' => 'code',
  198.             'client_id' => $client_id,
  199.             'scope' => 'openid profile',
  200.             'state' => $state,    
  201.         );
  202.  
  203.         $authorization_endpoint .= '?' . http_build_query($data);
  204.         header('Location: ' . $authorization_endpoint);
  205.         exit();
  206.     }
  207.  
  208. } else {
  209.     // Authorization error
  210.     exit("Authorization error : {$_GET['error']} : {$_GET['error_description']}");
  211. }

Télécharger

Notons que, dans cet exemple, nous avons intégré la clé publique dans le code. Une alternative consiste à se la procurer auprès d’AuthSD, voyez : API OpenId Connect : Point d’extrémité d’informations sur les clefs (Keys Endpoint). Mais la meilleure pratique est de valider le jeton par introspection.

Lancer le script : https://oa.dnc.global/oidc/tests/te...

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

Voici la page de login générée par le serveur OAuthSD :

 

A propos du scope sli : vous noterez que le contrôleur Authorize est appelé avec le scope sli, alors que ce n’est pas le cas dans l’exemple suivant. Cela permet d’illustrer le fonctionnement de la Connexion unique (Single Login In, SLI).



Deuxième version : Validation du JWT par Introspection

La vérification du jeton JWT est effectuée par la méthode décrite dans cet article : API Open ID Connect : Introspection.

PHP

  1. <?php
  2. /*
  3. testopenid4.php
  4.  
  5. Verbose Test of OpenID Connect JWT Introspection
  6.  
  7. This code is meant for testing, not for production !
  8.  
  9. For the authentification step, if you don't want to create an account
  10. on OAuthSD, use this credentials :
  11. login = bebert
  12. password = 012345678
  13.  
  14. Author :
  15. Bertrand Degoy https://degoy.com
  16. Credits :
  17. bschaffer https://github.com/bshaffer/oauth2-server-php
  18.  
  19. Licence : MIT licence
  20. Copyright (c) 2016 - DnC
  21.  
  22. */
  23.  
  24. $client_id = 'testopenid4';
  25. $client_secret = 'thesecret';
  26.  
  27. $authorization_endpoint = 'https://oa.dnc.global/authorize';
  28. $token_endpoint = 'https://oa.dnc.global/token';
  29. $introspection_endpoint = 'https://oa.dnc.global/introspect';
  30. $userinfo_endpoint = 'https://oa.dnc.global/userinfo';
  31.  
  32. //*** End of configuration ***
  33.  
  34. ini_set('display_errors', 1);
  35.  
  36. // Set session
  37. session_save_path('/home/oadnc/sessions_oauthsd');          
  38. session_name('oauthsd');
  39.  
  40.  
  41. if ( !isset($_GET['error']) ) {
  42.  
  43.     if ( isset($_GET['code']) ) {
  44.  
  45.         if ( isset($_GET['state']) ) {
  46.  
  47.             // Check state
  48.             if ( $_GET['state'] == $_SESSION['state'] ) {
  49.  
  50.                 // Step 2. Token request
  51.  
  52.                 $code = $_GET['code'];
  53.  
  54.                 $data = array(
  55.                     'grant_type' => 'authorization_code',
  56.                     'code' => $code,
  57.                 );
  58.                 $h = curl_init($token_endpoint);
  59.                 curl_setopt($h, CURLOPT_RETURNTRANSFER, true);
  60.                 curl_setopt($h, CURLOPT_TIMEOUT, 10);
  61.                 curl_setopt($h, CURLOPT_USERPWD, "{$client_id}:{$client_secret}");
  62.                 curl_setopt($h, CURLOPT_POST, true);
  63.                 curl_setopt($h, CURLOPT_HTTPHEADER, array('Content-Type:
  64.                            application/x-www-form-urlencoded'));
  65.                 curl_setopt($h, CURLOPT_POSTFIELDS, http_build_query($data));
  66.                 //curl_setopt($h, CURLOPT_SSL_VERIFYPEER, false);
  67.  
  68.                 $res = curl_exec($h);
  69.  
  70.                 if ( is_array(json_decode($res, true) ) ) {
  71.  
  72.                     curl_close($h);
  73.                     $res = json_decode($res, true);
  74.                    
  75.                     $access_token = $res['access_token'];
  76.  
  77.                     if  ( empty($res['error'] ) ) {
  78.  
  79.                         // Validate signed JWT token using introspection
  80.                         //* Post Methode  
  81.                         $data1 = array(
  82.                             'token' => $res['id_token'],
  83.                         );
  84.                         $h = curl_init($introspection_endpoint);
  85.                         curl_setopt($h, CURLOPT_RETURNTRANSFER, true);
  86.                         curl_setopt($h, CURLOPT_TIMEOUT, 10);
  87.                         curl_setopt($h, CURLOPT_POST, true);
  88.                         curl_setopt($h, CURLOPT_HTTPHEADER, array('Content-Type:
  89.                                  application/x-www-form-urlencoded'));  
  90.                         curl_setopt($h, CURLOPT_POSTFIELDS, http_build_query($data1));
  91.                         //*/
  92.  
  93.                         $res = curl_exec($h);
  94.  
  95.                         if ( is_array(json_decode($res, true) ) ) {
  96.  
  97.                             curl_close($h);
  98.                             $jwt = json_decode($res, true);
  99.  
  100.                             if  ( empty($jwt['error'] ) ) {
  101.  
  102.                                 if ( $jwt['active'] == 'true' ) {
  103.  
  104.                                     // If Token Response is valid goto step 3
  105.                                     // Step 3. Get UserInfo
  106.  
  107.                                     /* Auth Header Methode
  108.                                     $headr = array();
  109.                                     $headr[] = 'Authorization: Bearer ' . $access_token;
  110.                                     $h = curl_init();
  111.                                     curl_setopt($h, CURLOPT_URL, $userinfo_endpoint);
  112.                                     curl_setopt($h, CURLOPT_RETURNTRANSFER, true);
  113.                                     curl_setopt($h, CURLOPT_TIMEOUT, 10);
  114.                                     curl_setopt($h, CURLOPT_HTTPHEADER, $headr);
  115.                                     //curl_setopt($h, CURLOPT_SSL_VERIFYPEER, false);
  116.                                     //*/
  117.  
  118.                                     //* Post Methode  
  119.                                     $data2 = array(
  120.                                         'access_token' => $access_token,
  121.                                     );
  122.                                     $h = curl_init($userinfo_endpoint);
  123.                                     curl_setopt($h, CURLOPT_RETURNTRANSFER, true);
  124.                                     curl_setopt($h, CURLOPT_TIMEOUT, 10);
  125.                                     curl_setopt($h, CURLOPT_POST, true);
  126.                                     curl_setopt($h, CURLOPT_HTTPHEADER, array('Content-Type:
  127.                                                application/x-www-form-urlencoded'));    
  128.                                     curl_setopt($h, CURLOPT_POSTFIELDS,
  129.                                                      http_build_query($data2));
  130.                                     //*/
  131.  
  132.                                     $res = curl_exec($h);
  133.  
  134.                                     if ( is_array(json_decode($res, true) ) ) {
  135.  
  136.                                         curl_close($h);
  137.                                         $res = json_decode($res, true);
  138.  
  139.                                         if  ( empty($res['error'] ) ) {
  140.  
  141.                                             // Check User ID
  142.                                             if ( $jwt['sub'] == $res['sub'] ) {
  143.  
  144.                                                 // Everithing Ok !
  145.                                                 echo "UserInfo Response:\n";
  146.                                                 print_r($res);
  147.  
  148.                                             } else  
  149.                                                 // User of ID Token doesn't match UserInfo's one
  150.                                                 exit('User mismatch, got : ' . $res['sub']);
  151.  
  152.                                         } else
  153.                                             // Token request error
  154.                                             exit ('UserInfo Request error : ' . $res['error'] . ' : '
  155.                                                             . $res['error_description']);
  156.  
  157.                                     } else {
  158.                                         if ( !empty($res) ) {
  159.                                             // script error ?
  160.                                             exit ('UserInfo script error : ' . $res);
  161.                                         } else {
  162.                                             // Curl error during UserInfo request
  163.                                             $error = curl_error($h);
  164.                                             curl_close($h);
  165.                                             exit ('UserInfo request Curl error : ' . $error );
  166.                                         }
  167.                                     }
  168.  
  169.                                 } else
  170.                                     // JWT is inactive
  171.                                     exit('Error : Invactive ID Token');
  172.  
  173.                             } else
  174.                                 // Invalid id_token
  175.                                 exit('Error : Invalid ID Token');
  176.  
  177.                         } else {
  178.                             if ( !empty($res) ) {
  179.                                 // script error ?
  180.                                 exit ('Introspection script error : ' . $res);
  181.                             } else {
  182.                                 // Curl error during Introspection request
  183.                                 $error = curl_error($h);
  184.                                 curl_close($h);
  185.                                 exit ('Introspection request Curl error : ' . $error );
  186.                             }
  187.                         }
  188.  
  189.                     } else {
  190.                         // Token request error
  191.                         exit ('Token request error : ' . $res['error'] . ' : '
  192.                                          . $res['error_description']);
  193.                     }
  194.  
  195.                 } else {
  196.                     if ( !empty($res) ) {
  197.                         // script error ?
  198.                         exit ('Token script error : ' . $res);
  199.                     } else {
  200.                         // Curl error during Token request
  201.                         $error = curl_error($h);
  202.                         curl_close($h);
  203.                         exit ('Token request Curl error : ' . $error );
  204.                     }
  205.                 }
  206.  
  207.             } else
  208.                 // Wrong State
  209.                 exit("Authorization error : incoherent State");
  210.  
  211.         } else
  212.             // Missing State
  213.             exit("Authorization error : missing State");
  214.  
  215.     } else {
  216.  
  217.         // Step 1. Authorization Code request
  218.  
  219.         @session_regenerate_id();
  220.         $state = session_id();
  221.         $_SESSION['state'] = $state;
  222.  
  223.         $data = array(
  224.             'response_type' => 'code',
  225.             'client_id' => $client_id,
  226.             'scope' => 'openid profile sli',
  227.             'state' => $state,    
  228.         );
  229.  
  230.         $authorization_endpoint .= '?' . http_build_query($data);
  231.         header('Location: ' . $authorization_endpoint);
  232.         exit();
  233.     }
  234.  
  235. } else {
  236.     // Authorization error
  237.     exit("Authorization error : {$_GET['error']} : {$_GET['error_description']}");
  238. }
  239.  
  240.  
  241. ?>

Télécharger

Lancer le script : https://oa.dnc.global/oidc/tests/te...