Accueil > OpenID Connect OAuth Server par DnC > Développer > OpenID Connect > OpenID Connect : Autorisation via un code (Authorization Code (...)

Authentification utilisant le flux de code d’autorisation OpenID Connect Extrait de la spécification

  publié le par DnC

Cette section explique comment effectuer l’authentification à l’aide du Flux du code d’autorisation.

Le diagramme ci-contre donne une idée plus détaillée de la suite des échanges entre les 3 pôles d’un flux de type Autorisation via un code. Cliquez sur l’image pour l’agrandir


La suite de cet article est une traduction d’un extrait de OpenID Connect Core 1.0 avec nos commentaires.

3.1. Authentification utilisant le flux de code d’autorisation OpenID Connect (Authorization Code Flow).

Cette section explique comment effectuer l’authentification à l’aide du Flux du code d’autorisation [1].

Lorsque vous utilisez le Flux du code d’autorisation, tous les jetons sont renvoyés du point d’extrémité du jeton [2].

Le Flux du code d’autorisation renvoie un code d’autorisation au client [3], qui peut alors l’échanger directement pour un jeton d’identification et un jeton d’accès. Cela offre l’avantage de ne pas exposer de jetons à l’agent utilisateur et éventuellement à d’autres applications malveillantes avec accès à l’agent utilisateur. Le serveur d’autorisation peut également authentifier le client avant d’échanger le code d’autorisation d’un jeton d’accès. Le flux du code d’autorisation est adapté aux applications clientes qui peuvent maintenir en toute sécurité un secret entre eux et le serveur d’autorisation.

3.1.1. Étapes du flux d’autorisation

Le Flux du code d’autorisation passe par les étapes suivantes :

- Le client prépare une demande d’authentification contenant les paramètres de demande souhaités.
- Le client envoie la demande au serveur d’autorisation.
- Le serveur d’autorisation authentifie l’utilisateur final.
- Le serveur d’autorisation obtient le consentement / l’autorisation de l’utilisateur final [4]
- Le serveur d’autorisation renvoie l’utilisateur final au client avec un code d’autorisation.
- Le client s’adresse au nœud final de jeton en utilisant le code d’autorisation afin d’obtenir les jetons.
- Le client reçoit une réponse qui contient un jeton d’identification et un jeton d’accès dans le corps de réponse.
- Le client valide le jeton d’ID et récupère l’identifiant du sujet de l’utilisateur final.

3.1.2. Point d’extrémité Autorization

Le point d’extrémité d’autorisation effectue l’authentification de l’utilisateur final. Pour ce faire, l’agent utilisateur doit être envoyé au point d’extrémité d’autorisation pour authentification et autorisation du serveur d’autorisations à l’aide des paramètres de requête définis par OAuth 2.0 et de paramètres et valeurs supplémentaires définis par OpenID Connect.

La communication avec le noeud final d’autorisation DOIT utiliser TLS. Voir Section 16.17 pour plus d’informations sur l’utilisation de TLS.

3.1.2.1. Paramètres d’autorisation

Les serveurs d’autorisation DOIVENT prendre en charge l’utilisation des méthodes HTTP GET et POST définies dans le RFC 2616 [RFC2616] au niveau du point d’extrémité d’autorisation. Les clients PEUVENT utiliser les méthodes HTTP GET ou POST pour envoyer la demande d’autorisation au serveur d’autorisation. Si vous utilisez la méthode HTTP GET, les paramètres de requête sont sérialisés à l’aide de la sérialisation de chaîne de requête URI, conformément à la Section 13.1. Si vous utilisez la méthode HTTP POST, les paramètres de demande sont sérialisés à l’aide de la sérialisation de formulaire, conformément à la Section 13.2.

OpenID Connect utilise les paramètres de requête OAuth 2.0 suivants avec le flux de codes d’autorisation :

- scope
CHAMPS OBLIGATOIRES. Les demandes OpenID Connect DOIVENT contenir la valeur de la portée d’autorisation openid. Si la valeur openid n’est pas présente, le comportement est entièrement non spécifié. D’autres valeurs de domaine PEUVENT être présentes. Les valeurs de portée utilisées qui ne sont pas comprises par une implémentation DEVRAIENT être ignorées. Voir les sections 5.4 et 11 pour les valeurs de portée supplémentaires définies par cette spécification.

- response_type
CHAMPS OBLIGATOIRES. OAuth 2.0 Response Type Valeur qui détermine le flux de traitement des autorisations à utiliser, y compris les paramètres renvoyés par les ordinateurs d’extrémité utilisés. Lorsque vous utilisez le flux de codes d’autorisation, cette valeur est "code".

- client_id
CHAMPS OBLIGATOIRES. Identificateur de client OAuth 2.0 valide sur le serveur d’autorisations.

- redirect_uri
CHAMPS OBLIGATOIRES. URI de redirection auquel la réponse sera envoyée. Cet URI DOIT correspondre exactement à l’une des valeurs d’URI de redirection pour le client préenregistré auprès du fournisseur OpenID, la correspondance étant effectuée comme décrit dans le paragraphe 6.2.1 de la [RFC3986] (comparaison de chaînes simples). Lors de l’utilisation de ce flux, l’URI de redirection DEVRAIT utiliser le schéma https; Cependant, il PEUT utiliser le schéma http, à condition que le type de client soit confidentiel, tel que défini dans la section 2.1 de OAuth 2.0, et que le PO autorise l’utilisation des URI de redirection http dans ce cas. L’URI de redirection PEUT utiliser un autre schéma, tel qu’un schéma destiné à identifier un rappel dans une application native.

- state
CONSEILLÉ [5]. Valeur opaque utilisée pour conserver l’état entre la demande et le rappel. En règle générale, l’atténuation de la falsification de requêtes intersites (CSRF, XSRF) s’effectue en liant de manière cryptographique la valeur de ce paramètre à un cookie de navigateur.

OpenID Connect utilise également le paramètre de requête OAuth 2.0 suivant, défini dans la section Pratiques de codage de types de réponses multiples de OAuth 2.0 [OAuth.Responses] :

- response_mode
OPTIONNEL. Informe le serveur d’autorisations du mécanisme à utiliser pour renvoyer des paramètres à partir du point d’extrémité d’autorisation. Cette utilisation de ce paramètre n’est PAS RECOMMANDÉE lorsque le mode de réponse qui serait demandé est le mode par défaut spécifié pour le type de réponse.

Cette spécification définit également les paramètres de requête suivants :

- nonce
OPTIONNEL. Valeur de chaîne utilisée pour associer une session client à un jeton d’ID et pour limiter les attaques de relecture. La valeur est transmise non modifiée de la demande d’authentification au jeton d’ID. Une entropie suffisante DOIT être présente dans les valeurs nonce utilisées pour empêcher les attaquants de deviner des valeurs. Pour les notes sur la mise en œuvre, voir la section 15.5.2.

- display
OPTIONNEL. Valeur de chaîne ASCII indiquant comment le serveur d’autorisations affiche les pages d’interface utilisateur d’authentification et de consentement à l’utilisateur final. Les valeurs définies sont :

  • page
    Le serveur d’autorisation DEVRAIT afficher l’interface utilisateur d’authentification et de consentement cohérente avec une vue complète de la page de l’agent d’utilisateur. Si le paramètre d’affichage n’est pas spécifié, il s’agit du mode d’affichage par défaut.
  • popup
    Le serveur d’autorisation DEVRAIT afficher l’interface utilisateur d’authentification et de consentement cohérente avec une fenêtre d’agent d’utilisateur contextuelle. La fenêtre de l’agent d’utilisateur contextuelle doit être d’une taille appropriée pour une boîte de dialogue axée sur la connexion et ne doit pas masquer toute la fenêtre sur laquelle elle apparaît.
  • touch
    Le serveur d’autorisation DEVRAIT afficher une interface utilisateur d’authentification et de consentement cohérente avec un dispositif utilisant une interface tactile.
  • wap
    Le serveur d’autorisations DEVRAIT afficher l’interface utilisateur d’authentification et de consentement cohérente avec un affichage de type "smartphone".
    Le serveur d’autorisations PEUT aussi tenter de détecter les capacités de l’agent d’utilisateur et présenter un affichage approprié.

- prompt
OPTIONNEL. Liste de valeurs [6] de chaînes ASCII, séparées par des espaces et respectant la casse, spécifiant si le serveur d’autorisations invite l’utilisateur final à demander une nouvelle authentification et son consentement. Les valeurs définies sont :

  • none
    Le serveur d’autorisations NE DOIT PAS afficher de pages d’interface utilisateur d’authentification ou de consentement. Une erreur est renvoyée si un utilisateur final n’est pas déjà authentifié ou si le client ne dispose pas du consentement préconfiguré pour les revendications demandées ou ne remplit pas les autres conditions de traitement de la demande. Le code d’erreur sera généralement login_required, interaction_required ou un autre code défini dans la section 3.1.2.6. Ceci peut être utilisé comme méthode pour vérifier l’authentification et / ou le consentement existants. [7]
  • login
    Le serveur d’autorisation DEVRAIT demander à l’utilisateur final une nouvelle authentification. S’il ne peut pas réauthentifier l’utilisateur final, il DOIT retourner une erreur, généralement login_required.
  • consent
    Le serveur d’autorisation DEVRAIT demander à l’utilisateur final son consentement avant de retourner les informations au client. S’il ne peut pas obtenir le consentement, il DOIT retourner une erreur, généralement consent_required.
  • select_account
    Le serveur d’autorisation DEVRAIT demander à l’utilisateur final de sélectionner un compte d’utilisateur. Cela permet à un utilisateur final disposant de plusieurs comptes sur le serveur d’autorisations de choisir parmi plusieurs comptes pour lesquels il peut avoir des sessions en cours. S’il ne peut pas obtenir de choix de compte effectué par l’utilisateur final, il DOIT retourner une erreur, généralement account_selection_required.

- max_age
OPTIONNEL. Âge d’authentification maximum. Spécifie le temps écoulé autorisé en secondes depuis la dernière fois que l’utilisateur final a été authentifié activement par l’OP. Si le temps écoulé est supérieur à cette valeur, l’OP DOIT tenter de ré-authentifier activement l’utilisateur final. (Le paramètre max_age request correspond au paramètre de requête OpenID 2.0 PAPE [OpenID.PAPE] max_auth_age.) Lorsque max_age est utilisé, le jeton ID renvoyé DOIT inclure une valeur de déclaration auth_time.

- ui_locales
OPTIONNEL. Langues et scripts préférés de l’utilisateur final pour l’interface utilisateur, représentés par une liste de valeurs d’étiquette de langue BCP47 [RFC5646] séparées par des espaces, triées par préférence. Par exemple, la valeur "fr-CA fr en" représente une préférence pour le français parlé au Canada, puis le français (sans désignation de région), suivi de l’anglais (sans désignation de région). Une erreur NE DEVRAIT PAS se produire si certains ou tous les paramètres régionaux demandés ne sont pas pris en charge par le fournisseur OpenID.

- id_token_hint
OPTIONNEL. Le jeton ID précédemment émis par le serveur d’autorisations est transmis en tant qu’indicateur sur la session authentifiée actuelle ou passée de l’utilisateur final avec le client. Si l’utilisateur final identifié par le jeton ID est connecté ou est connecté par la requête, le serveur d’autorisations renvoie une réponse positive. sinon, il DEVRAIT retourner une erreur, telle que login_required. Si possible, un id_token_hint DEVRAIT être présent lorsque prompt = none est utilisé et une erreur invalid_request PEUT être renvoyée si ce n’est pas le cas ; Cependant, le serveur DEVRAIT répondre avec succès lorsque cela est possible, même s’il n’est pas présent. Authorization Server n’a pas besoin d’être répertorié en tant qu’audience du jeton ID lorsqu’il est utilisé en tant que valeur id_token_hint.
Si le jeton d’identification reçu par le RP en provenance de l’OP est chiffré, pour l’utiliser comme id_token_hint, le client DOIT déchiffrer le jeton d’identification signé contenu dans le jeton d’identification chiffré. Le client PEUT rechiffrer le jeton d’identification signé sur le serveur d’authentification à l’aide d’une clé permettant au serveur de déchiffrer le jeton d’ID et utiliser le jeton d’ID rechiffré en tant que valeur id_token_hint.

- login_hint
OPTIONNEL. Indication au serveur d’autorisations sur l’identifiant de connexion que l’utilisateur final peut utiliser pour se connecter (si nécessaire). Cet indice peut être utilisé par un RP s’il demande d’abord à l’adresse électronique (ou à un autre identifiant) de messagerie électronique, puis de transmettre cette valeur au conseil en tant qu’indicateur. Il est RECOMMANDÉ que la valeur de conseil corresponde à la valeur utilisée pour la découverte. Cette valeur PEUT aussi être un numéro de téléphone dans le format spécifié pour la revendication n ° de téléphone. L’utilisation de ce paramètre est laissée à la discrétion du PO.

- acr_values
OPTIONNEL. Valeur de "Requested Authentication Context Class Reference". Chaîne séparée par des espaces spécifiant les valeurs acr que le serveur d’autorisations est invité à utiliser pour traiter cette demande d’authentification, les valeurs apparaissant par ordre de préférence. La classe de contexte d’authentification satisfaite par l’authentification effectuée est renvoyée sous la valeur de revendication acr, comme spécifié à la section 2. La revendication acr est demandée en tant que revendication volontaire par ce paramètre.

D’autres paramètres PEUVENT être envoyés. Voir les sections 3.2.2, 3.3.2, 5.2, 5.5, 6 et 7.2.1 pour connaître les paramètres de demande d’autorisation et les valeurs de paramètre supplémentaires définis par cette spécification.

Notes

[1Dès l’origine, le rapprochement des termes "authentification" et "autorisation" crée la confusion. Avez-vous lu : Généralités sur l’authentification, introduction d’OpenID Connect ?

[2autrement dit, les jetons ne circulent que dans des relations serveur-serveur

[3l’application cliente, à ne pas confondre avec le navigateur du client final ou "agent utilisateur".

[4Toute la phraséologie d’OpenID Connect est déterminée par un cas d’utilisation particulier : celui où l’utilisateur final est propriétaire des données détenues par le serveur de ressource et auxquelles l’application souhaite accéder. Cependant, OpenID Connect peut également être utilisé pour authentifier l’utilisateur final et lui permettre d’accéder à des ressources qui ne lui appartiennent pas. Dans ce cas, la notion d’autorisation est inversée : c’est le propriétaire de l’application et des ressources protégées qui accorde l’accès.

[5Obligatoire pour OAuthSD

[6En réalité, toutes les combinaisons ne sont pas autorisées ou n’ont pas de sens. OAuthSD interprètera : ’none’, ’login’, ’login consent’, ’consent’.

[7La déclaration prompt=’none’ est utilisée notamment pour la procédure de ré-authentification silencieuse (Silent Re-authentication) et la surveillance de la validité de la session par iFrame. Voir : SSO et connexion unique (Single Login Identification, SLI), SSO et connexion unique (Single Login Identification, SLI) et Monitoring de l’état de l’authentification et SLO.

OpenID Connect : Obtenir une autorisation pour l’application cliente

  publié le par DnC

Dans le cadre du flux Autorisation avec Code (Authorization Code Flow) l’user-agent (en général un navigateur Web) de l’utilisateur final est redirigé sur le Point d’extrémité d’autorisation (Authorization Endpoint), pour permettre à l’utilisateur de s’identifier et d’accorder des autorisations à l’application cliente.
En cas de succès, l’user-agent sera redirigé sur le Point d’extrémité Token avec un code d’autorisation.

Point d’extrémité d’autorisation (Authorization Endpoint)

https://oa.dnc.global/authorize

Forme de la demande d’autorisation

Voici des exemples :

PHP

  1.     $data = array(
  2.         'response_type' => 'code',
  3.         'client_id' => 'chemin_openid',
  4.         'state' =>  $oauth_state,
  5.         'scope' => 'openid profile',
  6.     );
  7.  
  8. $authorization_endpoint = 'https://oa.dnc.global/authorization';
  9.  
  10. $authorization_endpoint .= '?' . http_build_query($data);
  11.     header('Location: ' . $authorization_endpoint);
  12.     exit();

Télécharger

SPIP

  1.     include_spip('inc/headers');
  2.    
  3.     $oauth_state = session_get('oauth_state');
  4.     $url = "http://oa.dnc.global/authorize?response_type=code&client_id=chemin_openid&scope=openid profile&redirect_uri=http://chemindeleau.com/callback_openid.php&state=$oauth_state";
  5.    
  6. redirige_par_entete($url);

Télécharger

Notes :
- Pour obtenir un jeton d’identité, le scope doit comporter "openid". Dans le cas contraire, la réponse sera identique à celle du protocole OAuth 2.0, et ne comprendra donc que le jeton d’accès.
- Pour obtenir un jeton de rafraîchissement (Refresh Token), le scope doit comporter "offline_access".
- Bien que la "norme" indique que le paramètre redirect_uri est obligatoire, il peut être omis si l’application cliente a été inscrite avec une seule adresse de retour.
- si l’application cliente a été inscrite avec plusieurs adresses de retour, le paramètre redirect_uri est obligatoire, et doit représenter l’une d’elles.
- Il est possible de rajouter à l’URL tout paramètre utile, comme un identificateur de session. Ceux-ci seront retransmis dans le corps de la réponse, de façon quasi intégrale.
- Avant qu’elle puisse interagir dans un flux OpenID Connect, l’auteur doit inscrire l’application cliente sur le serveur OAuthSD avec les paramètres attendus par OpenID Connect.
- Il est de la responsabilité de l’application cliente d’assurer la bonne forme et la sécurité des valeurs transmises par les paramètres d’URL.

Authentification de l’utilisateur final

A l’appel du Point d’extrémité d’autorisation :
- le serveur OAuthSD redirige l’user-agent vers la page d’authentification (on reste dans le domaine du serveur d’autorisation).
- l’utilisateur final s’authentifie dans cette page (les identifiants sont donc confinés au niveau du serveur).
- le serveur poste le code d’autorisation au Point d’extrémité de redirection.

Retour à l’application cliente

En cas de succès, le serveur redirige le navigateur sur le point d’extrémité de redirection dans l’application cliente ( par entête HTTP code 302 ). Cet URI est défini par l’auteur d’une application cliente quand il l’inscrit sur ce serveur. Voir : OpenID Connect : Lier une application cliente au serveur OAuthSD.

Les paramètres code et state sont passés dans l’URL. Exemple :

http://chemindeleau.com/callback_openid.php?code=3159339c2f1326f9fa128e161b8387feca690b65&state=98b3027139f7cb3be4a885d7c81b41bb

Il est de la responsabilité de l’application cliente d’assurer sa sécurité vis-à-vis des valeurs transmises par les paramètres d’URL.

Situations d’erreur

Se reporter à : API OpenID Connect : Point d’extrémité d’autorisation (Authorization Endpoint).

OpenID Connect : Obtenir les jetons d’accès

  publié le par DnC

Nous sommes dans le cas d’un flux d’Autorisation via un code (Authorization Code).

L’application cliente doit disposer d’un jeton d’accès pour obtenir des données protégées de la part d’un serveur de ressources. Le code nécessaire est entièrement à la charge de l’auteur de l’application cliente, en réaction à la redirection sur l’URI du point d’extrémité de redirection.

Pour obtenir les jetons d’accès, une application cliente s’adresse au point d’extrémité token avec le code obtenu dans la phase d’autorisation.

Point d’extrémité de jeton (Token Endpoint)

https://oa.dnc.global/token

Plus de détails : API OpenID Connect : Point d’extrémité d’autorisation (Authorization Endpoint).

Le point d’extrémité de jeton est le point d’extrémité sur le serveur d’autorisation auquel s’adresse l’application cliente avec le code d’autorisation.

Forme de la demande de jeton d’accès

La demande ne doit être effectuée que par la méthode POST.

Pour l’authentification de l’application cliente auprès du serveur d’autorisation, OAuthSD impose la méthode client_secret_basic. L’authentification est donc effectuée en utilisant l’authentification HTTP Basic (cf. section 2.3.1 de OAuth 2.0 [RFC6749]). Les identifiants client_id et client_secret sont ceux qui ont été définis lors de l’inscription de l’application cliente sur le serveur.

Les paramètres suivants doivent être postés :
- grant_type : Type de flux d’autorisation, doit être "authorization_code".
- code : le code d’autorisation reçu.
- redirect_uri : l’adresse de retour à l’application cliente.

Réponse du serveur

En cas de succès, le serveur retourne une réponse HTTP 200. Le corps de la réponse contient :

index type valeur
page JSON array access_token : (string) jeton d’accès OAuth 2.0
expires_in : (long) durée de vie en secondes
token_type : (string) "Bearer"
scope : (string) "openid ... "
id_token : (string) jeton d’identification (JWT)

Le Header comporte, comme il se doit, la directive ’Cache-Control : no-cache, no-store’.

 

En cas d’échec, le corps de la réponse contient :

index type valeur
page 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 sont décrites ici : API OpenID Connect : Point d’extrémité de jeton (Token Endpoint).

Demande du jeton de rafraîchissement

OpenID Connect ne retourne un jeton de rafraîchissement (Refresh Token), conjointement au jeton d’accès, que si le scope "offline_access" a été inclus dans la requête et accepté, ce qui ne se produira qu’avec le flux d’Autorisation via un code (Authorization Code Grant).

Exemples de code

Données de la requête :
- $authcode est le code d’autorisation obtenu à l’étape précédente et transmis à la page de CallBack
- $client_id, $client_secret : Comme indiqué lors de l’inscription de l’application cliente sur le serveur d’autorisation.

PHP

  1. // Demander un jeton d'accès pour l'application
  2.  
  3.     $url = 'http://oa.dnc.global/token';
  4.  
  5.     $datas =  array(
  6.         'grant_type' => 'authorization_code',
  7.         'code' => $authcode,
  8.         'redirect_uri' => 'http://chemindeleau.com/callback_openid.php',
  9.         'client_id' => 'chemin_openid',
  10.         'client_secret' => '01fc458',    
  11.     );        
  12.  
  13.     $ch = curl_init();
  14.  
  15.     curl_setopt($ch, CURLOPT_URL, $url);
  16.     curl_setopt($ch, CURLOPT_POST, true);
  17.     curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/x-www-form-urlencoded'));
  18.     curl_setopt($ch, CURLOPT_POSTFIELDS,  http_build_query($datas));
  19.     curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
  20.     curl_setopt($ch, CURLOPT_HEADER, false);
  21.     curl_setopt($ch, CURLOPT_TIMEOUT, 30);
  22.     curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
  23.  
  24.     $result_json = curl_exec($ch);
  25.     curl_close($ch);
  26.  
  27.     $result = json_decode($result_json, true);
  28.     $access_token = $result['access_token'];  // Access Token
  29.     $id_token = $result['id_token'];                    // ID Token (JWT)

Télécharger

L’authentification peut également être passée dans le Header comme ceci :

  1.     $datas =  array(
  2.         'grant_type' => 'authorization_code',
  3.         'code' => $sanitized_authcode,
  4.         'redirect_uri' => 'http://chemindeleau.com/callback_openid.php',
  5.     );
  6.    
  7.     $client_id = 'chemin_openid';
  8.     $client_secret = '01fc458';  
  9.  
  10.     $ch = curl_init();
  11.     curl_setopt($ch, CURLOPT_URL, $url);      
  12.     curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
  13.     curl_setopt($ch, CURLOPT_TIMEOUT, 30);
  14.     curl_setopt($ch, CURLOPT_USERPWD, "{$client_id}:{$client_secret}");
  15.     curl_setopt($ch, CURLOPT_POST, true);
  16.     curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/x-www-form-urlencoded'));
  17.     curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($datas));
  18.     curl_setopt($ch, CURLOPT_HEADER, false);
  19.     curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);

Télécharger

SPIP

  1.         $url = 'http://oa.dnc.global/oauth/token.php';
  2.         $options = array(
  3.             'method' => 'POST',
  4.             'datas' =>  array(
  5.                  'grant_type' => 'authorization_code',
  6.                  'code' => $authcode,
  7.                  'redirect_uri' => 'http://chemindeleau.com/callback_openid.php',
  8.                  'client_id' => 'chemin_openid',
  9.                  'client_secret' => '01fc458',  
  10.             )        
  11.         );
  12.  
  13.         $res = recuperer_url($url, $options);
  14.  
  15.         $page = json_decode($res['page'], true);
  16.  
  17.         $token = $page['access_token'];
  18.         $id_token = $page['id_token'];

Télécharger

Ré-authentification silencieuse du sujet avec l’ID Token

  publié le par DnC

L’authentification du sujet (subject, sub) avec l’ID Token est une option du flux de code OpenID Connect Authorization.
Pour remplir la certification OpenID Connect, il est nécessaire de terminer le test "OP-Req-id_token_hint".
La bibliothèque oauth2-server-php de Brent Shaffer ne traite pas id_token_hint. OAuthSD devra donc s’en occuper.

Le cas d’utilisation consiste à appeler Authorize à l’aide de prompt = ’none’ et à transmettre l’ID Token par le paramètre id_token_hint.

La spécification stipule :

id_token_hint
OPTIONNEL. Le jeton ID précédemment émis par le serveur d’autorisations est transmis en tant qu’identifiant de la session d’authentification actuelle ou passée de l’utilisateur final avec le client. Si l’utilisateur final identifié par le jeton ID est connecté ou est connecté par la requête, le serveur d’autorisations renvoie une réponse positive. sinon, il DEVRAIT retourner une erreur, telle que login_required. ... Le serveur d’autorisation n’a pas besoin d’être répertorié en tant qu’audience du jeton ID lorsqu’il est utilisé en tant que valeur id_token_hint.

Comment OAuthSD Implémente le traitement id_token_hint

Si l’appel à Authorize inclut le paramètre id_token_hint, nous vérifierons la signature du Jeton d’Identité JWT. Si Ok, nous adopterons la valeur de la déclaration sub pour définir l’identifiant de l’utilisateur final (user_id) et nous continuerons normalement avec le traitement de prompt = ’none’.
[dnc68]

Remarque de sécurité

Lorsque l’on fait une authentification avec prompt = ’none’, on devrait toujours y associer le paramètre id_token_hint. Plus exactement, interroger Authorize avec prompt = ’none’ sans id_token_hint ne peut être considéré comme une authentification de l’utilisateur, mais comme une simple information sur sa connexion.

Notes :
- Si user_id est défini par l’enregistrement du client, la déclaration sub du jeton d’identité devra être identique ou le processus échouera.
- prompt= ’none’ exclut l’invite de l’utilisateur. L’expression "ou est connecté par la requête" peut couvrir un mécanisme tel que la réauthentification silencieuse (SRA). C’est ce que OAuthSD permet de faire.

Voyez également :
- OpenID Connect : SSO, management de session etc..

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

  (publié initialement le lundi 2 janvier 2017) par DnC

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...