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

, par DnC

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

Cet article est une traduction d’un extrait de OpenID Connect Core 1.0

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. Paramètre d’autorisation

Le point d’extrémité d’autorisation (Authorization Endpoint) effectue l’authentification de l’utilisateur final. Cela se fait en envoyant l’agent utilisateur au point d’autorisation d’authentification et d’autorisation du serveur d’autorisation, en utilisant les paramètres de demande définis par OAuth 2.0 et les paramètres et valeurs de paramètres supplémentaires définis par OpenID Connect.

La communication avec le point d’extrémité d’autorisation DOIT utiliser TLS. Voir Section 16.17 pour plus d’informations sur l’utilisation de TLS.

3.1.2.1. Demande d’authentification

Une demande d’authentification est une demande d’autorisation OAuth 2.0 qui demande que l’utilisateur final soit authentifié par le serveur d’autorisation.

Les serveurs d’autorisation DOIVENT soutenir l’utilisation des méthodes HTTP GET et POST définies dans la RFC 2616 [RFC2616] au point d’arrivée 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 la requête sont sérialisés en utilisant la sérialisation de la chaîne d’interrogation URI, conformément à la section 13.1. Si vous utilisez la méthode POST HTTP, les paramètres de la requête sont sérialisés à l’aide de la sérialisation des formulaires, conformément à la section 13.2.

OpenID Connect utilise les paramètres de demande OAuth 2.0 suivants avec le flux de code d’autorisation :

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

response_type
OBLIGATOIRE. Valeur de Type de réponse OAuth 2.0 qui détermine le flux de traitement d’autorisation à utiliser, y compris les paramètres renvoyés par les points d’extrémité utilisés. Lorsque vous utilisez le Flux du code d’autorisation, cette valeur est "code".

client_id
OBLIGATOIRE. OAuth 2.0 Client Identifier valide sur le serveur d’autorisation.

redirect_uri
OBLIGATOIRE. 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ées sur le fournisseur OpenID, la correspondance étant effectuée comme décrit dans la section 6.2.1 de [RFC3986] (Simple String Comparison). Lorsque vous utilisez ce flux, l’URI de redirection DOIT 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 d’OAuth 2.0, et pourvu que l’OP permette l’utilisation d’URI de Redirection HTTP dans ce cas. L’URI de redirection PEUT utiliser un autre schéma, tel que celui qui est destiné à identifier un rappel dans une application native.

state
CONSEILLÉ [5]. Valeur opaque utilisée pour maintenir l’état entre la demande et le rappel. Généralement, l’atténuation Cross-Site Request Forgery (CSRF, XSRF) est effectuée par liaison cryptographique de la valeur de ce paramètre avec un cookie de navigateur. Ce paramètre est obligatiore avec OAuth Server by DnC.

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

response_mode
OPTIONNEL. Informe le serveur d’autorisation du mécanisme à utiliser pour renvoyer les paramètres à partir du point d’extrémité d’autorisation. Cette utilisation de ce paramètre n’est PAS RECOMMANDÉ 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 de manière 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 les 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’autorisations DEVRAIT afficher l’interface utilisateur d’authentification et de consentement cohérente avec un appareil 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 de chaîne 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 [6]
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 de l’autorisation préconfigurée 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 à la section 3.1.2.6. Cela peut être utilisé comme une méthode pour vérifier l’authentification et / ou le consentement existant.

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

Le paramètre prompt peut être utilisé par le client pour s’assurer que l’utilisateur final est toujours présent pour la session en cours ou pour attirer l’attention sur la demande. Si ce paramètre ne contient aucun élément avec une autre valeur, une erreur est renvoyée.

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 revendication 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’étiquettes 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é qui permet au serveur de déchiffrer le jeton d’ID et utiliser le jeton d’ID rechiffré comme 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 souhaite transmettre cette valeur au service d’autorisation découverte. Il est RECOMMANDÉ que la valeur de conseil corresponde à la valeur utilisée pour la découverte. Cette valeur PEUT également ê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 de l’OP.

acr_values
OPTIONNEL. Requested Authentication Context Class Reference values. 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 dans le cas d’OAuthSD.

[6La 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.

OpenID Connect : Obtenir une autorisation pour l’application cliente

, par DnC

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

https://oa.dnc.global/authorize

Le point d’extrémité d’autorisation est le point d’extrémité sur le serveur d’autorisation auquel l’utilisateur final se connecte à l’aide de l’user-agent (en général un navigateur Web), et accorde l’autorisation à l’application cliente.

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.

Voici un exemple de page d’authentification :

http://oa.dnc.global/authorize?response_type=code&client_id=chemin_openid&redirect_uri=http://chemindeleau.com/callback_openid.php&state=xyz

Notes :
- si l’on souhaite afficher la page d’authentification dans le contexte visuel de l’application cliente, il faut le faire dans un iFrame, plutôt que par une insertion en script côté client (Popup Javascript), afin de ne pas compromettre les identifiants de l’utilisateur. Cependant, il convient d’appliquer les bonnes pratiques de sécurité relatives à la mise en oeuvre des iFrame.
- Les règles CORS doivent être observées pour autoriser les aller-retours entre le domaines de l’application cliente et celui du serveur OAuth. OAuth Server by DnC assure une configuration standard de son côté. Il se peut qu’il y ait également des paramétrages à faire côté client . Voyez Contrôle d’accès HTTP (CORS).

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

En cas d’échec, on reste sur le serveur d’autorisation et 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 :

Réponse error
titre de l’erreur
error_description
description de l’erreur
Explication
400 invalid_client No client id supplied Le paramètre client_id est obligatoire.
400 invalid_client The client id supplied is invalid Le paramètre client_id doit être identique à ce qui a été inscrit pour l’application.
400 invalid_uri The redirect URI must not contain a fragment
400 invalid_uri No redirect URI was supplied or stored
400 invalid_uri A redirect URI must be supplied when multiple redirect URIs are registered
400 redirect_uri_mismatch The redirect URI provided is missing or does not match Le paramètre redirect_uri doit être identique à ce qui a été inscrit pour l’application.
400 invalid_uri The redirect URI must not contain a fragment
401 not_allowed L’utilisateur final a probablement fait une erreur dans ses identifiants. Recommencer.
401 malformed_identifier L’utilisateur final a fourni un identifiant (e-mail ou pseudo) mal formé. Recommencer.
401 malformed_email Un E-mail est requis en guise d’identifiant de l’utilisateur final. L’utilisateur final a probablement fait une erreur. Recommencer.
403 consent_required The user denied access to your application L’utilisateur a refusé son accord ou a abandonné l’authentification.
403 forbidden Probablement une attaque de type "Man in the middle". Ne pas réagir.

Si le serveur retourne une erreur 401, il est de la responsabilité de l’application cliente de présenter un message d’erreur à l’utilisateur final et de recommencer (ou non) l’authentification. Dans tous les autres cas d’erreur, l’application cliente ne devrait pas redemender l’authentification avant d’avoir identifié l’erreur au niveau technique.

Dans certains cas, la redirection peut être effectuée, le corps de la réponse contenant un message d’erreur :

error
titre de l’erreur
error_description
description de l’erreur
Explication
invalid_request Invalid or missing response type response_type doit être "code"
unauthorized_client The grant type is unauthorized for this client_id Vérifiez que l’application a bien été inscrite avec le type d’autorisation Authorization_code
redirect_uri_mismatch The redirect URI is mandatory and was not supplied Le paramètre redirect_uri doit figurer dans la requête.

OpenID Connect : Obtenir les jetons d’accès

, 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

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 :

Réponse error
titre de l’erreur
error_description
description de l’erreur
Explication
405 invalid_request The request method must be POST when requesting an access token La méthode de la demande doit être POST lorsque vous demandez un jeton d’accès. Notez que le Header contient ’Allow : POST’
400 invalid_request The grant type was not specified in the request Le type d’autorisation n’a pas été spécifié dans la demande
400 unsupported_grant_type Grant type "X" not supported Le Type d’autorisation "X" n’est pas supporté.
400 invalid_grant Authorization code doesn\’t exist or is invalid for the client Le code d’autorisation n’existe pas ou est invalide pour le client. Cette situation peut résulter du fait que le code a déjà été utilisé pour une demande de jeton.
400 unauthorized_client The grant type is unauthorized for this client_id Le type d’autorisation n’est pas autorisé pour ce client_id
400 invalid_scope The scope requested is invalid for this request La portée d’autorisation demandée est invalide pour cette demande
400 invalid_scope The scope requested is invalid for this client La portée d’autorisation demandée est valide pour ce client
400 invalid_scope An unsupported scope was requested La portée d’autorisation demandée n’est pas supportée

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

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

, 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_name = session_name();
  59. if ( empty($session_name) ) {
  60. session_name('PHPSESSID');
  61. }
  62. if ( isset($_GET['state']) ) session_id($_GET['state']);
  63.  
  64.  
  65. if ( !isset($_GET['error']) ) {
  66.  
  67. if ( isset($_GET['code']) ) {
  68.  
  69. if ( isset($_GET['state']) ) {
  70.  
  71. // Check state
  72. if ( $_GET['state'] == $_SESSION['state'] ) {
  73.  
  74. // Step 2. Token request
  75.  
  76. $code = $_GET['code'];
  77.  
  78. $data = array(
  79. 'grant_type' => 'authorization_code',
  80. 'code' => $code,
  81. );
  82. $h = curl_init($token_endpoint);
  83. curl_setopt($h, CURLOPT_RETURNTRANSFER, true);
  84. curl_setopt($h, CURLOPT_TIMEOUT, 10);
  85. curl_setopt($h, CURLOPT_USERPWD, "{$client_id}:{$client_secret}");
  86. curl_setopt($h, CURLOPT_POST, true);
  87. curl_setopt($h, CURLOPT_HTTPHEADER,
  88. array('Content-Type: application/x-www-form-urlencoded'));
  89. curl_setopt($h, CURLOPT_POSTFIELDS, http_build_query($data));
  90. //curl_setopt($h, CURLOPT_SSL_VERIFYPEER, false);
  91.  
  92. $res = curl_exec($h);
  93.  
  94. if ($res) {
  95.  
  96. $res = json_decode($res, true);
  97.  
  98. if ( empty($res['error'] ) ) {
  99.  
  100. // Validate signed JWT token using client's public key
  101. if ( $payload = decode_jwt($res['id_token'],
  102. PUBLIC_KEY, 'RS256') ) {
  103.  
  104. // If Token Response is valid goto step 3
  105. // Step 3. Get UserInfo
  106. $access_token = $res['access_token'];
  107.  
  108. /* Auth Header Methode
  109.   $headr = array();
  110.   $headr[] = 'Authorization: Bearer ' . $access_token;
  111.   $h = curl_init();
  112.   curl_setopt($h, CURLOPT_URL, $userinfo_endpoint);
  113.   curl_setopt($h, CURLOPT_RETURNTRANSFER, true);
  114.   curl_setopt($h, CURLOPT_TIMEOUT, 10);
  115.   curl_setopt($h, CURLOPT_HTTPHEADER, $headr);
  116.   //curl_setopt($h, CURLOPT_SSL_VERIFYPEER, false);
  117.   //*/
  118.  
  119. //* Post Methode
  120. $data2 = array(
  121. 'access_token' => $access_token,
  122. );
  123. $h = curl_init($userinfo_endpoint);
  124. curl_setopt($h, CURLOPT_RETURNTRANSFER, true);
  125. curl_setopt($h, CURLOPT_TIMEOUT, 10);
  126. curl_setopt($h, CURLOPT_POST, true);
  127. curl_setopt($h, CURLOPT_HTTPHEADER,
  128. array('Content-Type:
  129. application/x-www-form-urlencoded'));
  130. curl_setopt($h, CURLOPT_POSTFIELDS,
  131. http_build_query($data2));
  132. //*/
  133.  
  134. $res = curl_exec($h);
  135.  
  136. if ( $res ) {
  137.  
  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. }
  162.  
  163. } else
  164. // Invalid id_token
  165. exit('Error : Invalid ID Token');
  166.  
  167. } else {
  168. // Token request error
  169. exit ('Token request error : ' . $res['error'] . ' : '
  170. . $res['error_description']);
  171. }
  172.  
  173. } else {
  174. // Curl error during Token request
  175. exit('Token Request error : ' . curl_error($h));
  176. }
  177.  
  178. } else
  179. // Wrong State
  180. exit("Authorization error : incoherent State");
  181.  
  182. } else
  183. // Missing State
  184. exit("Authorization error : missing State");
  185.  
  186. } else {
  187.  
  188. // Step 1. Authorization Code request
  189.  
  190. $state = session_id();
  191. $_SESSION['state'] = $state;
  192.  
  193. $data = array(
  194. 'response_type' => 'code',
  195. 'client_id' => $client_id,
  196. 'scope' => 'openid profile',
  197. 'state' => $state,
  198. );
  199.  
  200. $authorization_endpoint .= '?' . http_build_query($data);
  201. header('Location: ' . $authorization_endpoint);
  202. exit();
  203. }
  204.  
  205. } else {
  206. // Authorization error
  207. exit("Authorization error : {$_GET['error']} : {$_GET['error_description']}");
  208. }

Télécharger

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

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 :

 

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 : 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_name = session_name();
  38. if ( empty($session_name) ) {
  39. session_name('PHPSESSID');
  40. }
  41. if ( isset($_GET['state']) ) session_id($_GET['state']);
  42.  
  43.  
  44. if ( !isset($_GET['error']) ) {
  45.  
  46. if ( isset($_GET['code']) ) {
  47.  
  48. if ( isset($_GET['state']) ) {
  49.  
  50. // Check state
  51. if ( $_GET['state'] == $_SESSION['state'] ) {
  52.  
  53. // Step 2. Token request
  54.  
  55. $code = $_GET['code'];
  56.  
  57. $data = array(
  58. 'grant_type' => 'authorization_code',
  59. 'code' => $code,
  60. );
  61. $h = curl_init($token_endpoint);
  62. curl_setopt($h, CURLOPT_RETURNTRANSFER, true);
  63. curl_setopt($h, CURLOPT_TIMEOUT, 10);
  64. curl_setopt($h, CURLOPT_USERPWD, "{$client_id}:{$client_secret}");
  65. curl_setopt($h, CURLOPT_POST, true);
  66. curl_setopt($h, CURLOPT_HTTPHEADER, array('Content-Type:
  67. application/x-www-form-urlencoded'));
  68. curl_setopt($h, CURLOPT_POSTFIELDS, http_build_query($data));
  69. //curl_setopt($h, CURLOPT_SSL_VERIFYPEER, false);
  70.  
  71. $res = curl_exec($h);
  72.  
  73. if ( is_array(json_decode($res, true) ) ) {
  74.  
  75. $res = json_decode($res, true);
  76.  
  77. $access_token = $res['access_token'];
  78.  
  79. if ( empty($res['error'] ) ) {
  80.  
  81. // Validate signed JWT token using introspection
  82. //* Post Methode
  83. $data1 = array(
  84. 'token' => $res['id_token'],
  85. );
  86. $h = curl_init($introspection_endpoint);
  87. curl_setopt($h, CURLOPT_RETURNTRANSFER, true);
  88. curl_setopt($h, CURLOPT_TIMEOUT, 10);
  89. curl_setopt($h, CURLOPT_POST, true);
  90. curl_setopt($h, CURLOPT_HTTPHEADER, array('Content-Type:
  91. application/x-www-form-urlencoded'));
  92. curl_setopt($h, CURLOPT_POSTFIELDS, http_build_query($data1));
  93. //*/
  94.  
  95. $res = curl_exec($h);
  96.  
  97. if ( is_array(json_decode($res, true) ) ) {
  98.  
  99. $jwt = json_decode($res, true);
  100.  
  101. if ( empty($jwt['error'] ) ) {
  102.  
  103. if ( $jwt['active'] == 'true' ) {
  104.  
  105. // If Token Response is valid goto step 3
  106. // Step 3. Get UserInfo
  107.  
  108. /* Auth Header Methode
  109.   $headr = array();
  110.   $headr[] = 'Authorization: Bearer ' . $access_token;
  111.   $h = curl_init();
  112.   curl_setopt($h, CURLOPT_URL, $userinfo_endpoint);
  113.   curl_setopt($h, CURLOPT_RETURNTRANSFER, true);
  114.   curl_setopt($h, CURLOPT_TIMEOUT, 10);
  115.   curl_setopt($h, CURLOPT_HTTPHEADER, $headr);
  116.   //curl_setopt($h, CURLOPT_SSL_VERIFYPEER, false);
  117.   //*/
  118.  
  119. //* Post Methode
  120. $data2 = array(
  121. 'access_token' => $access_token,
  122. );
  123. $h = curl_init($userinfo_endpoint);
  124. curl_setopt($h, CURLOPT_RETURNTRANSFER, true);
  125. curl_setopt($h, CURLOPT_TIMEOUT, 10);
  126. curl_setopt($h, CURLOPT_POST, true);
  127. curl_setopt($h, CURLOPT_HTTPHEADER, 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 ( is_array(json_decode($res, true) ) ) {
  136.  
  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. exit ('UserInfo request Curl error : ' . $error );
  165. }
  166. }
  167.  
  168. } else
  169. // JWT is inactive
  170. exit('Error : Invactive ID Token');
  171.  
  172. } else
  173. // Invalid id_token
  174. exit('Error : Invalid ID Token');
  175.  
  176. } else {
  177. if ( !empty($res) ) {
  178. // script error ?
  179. exit ('Introspection script error : ' . $res);
  180. } else {
  181. // Curl error during Introspection request
  182. $error = curl_error($h);
  183. exit ('Introspection request Curl error : ' . $error );
  184. }
  185. }
  186.  
  187. } else {
  188. // Token request error
  189. exit ('Token request error : ' . $res['error'] . ' : '
  190. . $res['error_description']);
  191. }
  192.  
  193. } else {
  194. if ( !empty($res) ) {
  195. // script error ?
  196. exit ('Token script error : ' . $res);
  197. } else {
  198. // Curl error during Token request
  199. $error = curl_error($h);
  200. exit ('Token request Curl error : ' . $error );
  201. }
  202. }
  203.  
  204. } else
  205. // Wrong State
  206. exit("Authorization error : incoherent State");
  207.  
  208. } else
  209. // Missing State
  210. exit("Authorization error : missing State");
  211.  
  212. } else {
  213.  
  214. // Step 1. Authorization Code request
  215.  
  216. $state = session_id();
  217. $_SESSION['state'] = $state;
  218.  
  219. $data = array(
  220. 'response_type' => 'code',
  221. 'client_id' => $client_id,
  222. 'scope' => 'openid profile',
  223. 'state' => $state,
  224. );
  225.  
  226. $authorization_endpoint .= '?' . http_build_query($data);
  227. header('Location: ' . $authorization_endpoint);
  228. exit();
  229. }
  230.  
  231. } else {
  232. // Authorization error
  233. exit("Authorization error : {$_GET['error']} : {$_GET['error_description']}");
  234. }
  235.  
  236.  
  237. ?>

Télécharger

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