OpenID Connect est un protocole d’authentification construit au-dessus de OAuth qui en constitue la couche de délégation d’autorisations.

Généralités sur l’authentification, introduction d’OpenID Connect Ou la nécessité de compléter OAuth

, par DnC

OpenID Connect est une couche d’identité au dessus du protocole de délégation d’autorisation OAuth 2.0 permettant aux applications :
- de vérifier l’identité de l’usager
- d’obtenir de façon interopérable (avec REST) des informations de base (attributs) concernant le profil de l’usager.

On croit souvent que l’usage d’OpenID Connect impose d’utiliser un compte Google, Facebook ou Twitter etc. OAuth Server by DnC implémente OpenID Connect sur un serveur privé, le vôtre éventuellement, et offre donc la confidentialité d’une alternative indépendante.

OAuth est-il un système d’authentification ?

L’authentification suppose que l’application protégée, qui reçoit une autorisation de délivrer les informations à l’application cliente, puisse identifier l’utilisateur final qui est à l’origine de l’autorisation.
OAuth ne fournit pas cet identité et doit être complété pour assurer l’authentification.

OpenID Connect

OpenID Connect est une couche d’authentification construite sur OAuth 2.0. Il s’agit à la fois d’un fournisseur de profil et d’une extension d’OAuth. Le serveur d’autorisation (ou "fournisseur OpenID" dans ce cas) contient des informations sur une personne. OAuth est utilisé pour protéger ces informations, permettant à une application tierce (client) d’y accéder au nom d’une personne. Pour autoriser la diffusion d’informations, la personne est authentifiée et le Fournisseur OpenID fournit au client des détails notamment sur l’identité de la personne et le moment de l’authentification.

En plus du jeton d’accès, OpenID Connect met en oeuvre le jeton JWT (JSON Web Token) qui contient des informations sur l’utilisateur authentifié. Le jeton d’identification est signé par le serveur d’autorisation et peut être lu et vérifié sans accéder au serveur d’autorisation. C’est cette caractéristique qui doit conduire à choisir OpenID Connect, en réponse à la problématique de la Validation du jeton d’accès par une ressource protégée.

En outre, OpenID Connect standardise différentes choses que OAuth laisse au choix du développeur. Par exemple les scopes, la découverte des points d’extrémité et l’enregistrement dynamique des clients.

Process du protocole OpenID Connect

JPEG - 220.1 ko

Cliquez sur la vignette.


OpenID Connect : Types d’autorisations (Grant Type)

, par DnC

Dans le cadre de cette documentation du protocole OpenID Connect, nous ne traiterons que le type d’autorisation suivant :
- Autorisation via un code (Authorization Code Grant).

Nous ne développons pas les autorisations Implicit et Hybrid, ces méthodes étant jugées non sûres et par ailleurs assez peu utilisées.

Flux de code d’autorisation (Authorisation code flow) - le flux le plus couramment utilisé, destiné aux applications Web traditionnelles ainsi qu’aux applications natives / mobiles. Implique une redirection initiale du navigateur vers le serveur d’authentification pour l’authentification et le consentement de l’utilisateur, puis une seconde demande de l’application cliente pour récupérer le jeton d’ID. Ce flux offre une sécurité optimale, car les jetons ne sont pas révélés au navigateur et l’application cliente peut également être authentifiée.

Le diagramme de ce flux est identique à son homologue d’OAuth 2.0, la différence étant le jeton d’identification (ID Token) qui accompagne le jeton d’accès. La signature de ce jeton offre aux serveurs de ressource protégée (RS) l’opportunité de valider le jeton localement, pourvu qu’une forme de clé leur soit connue.

La mise en œuvre de ce flux dans le cadre d’OAuthSD est détaillée ici : OpenID Connect : Autorisation via un code (Authorization Code Flow).

Nous mentionnerons simplement les deux autres méthodes :

Ces flux exposent les jetons au navigateur de l’utilisateur final, ce qui rend possible une exploitation par un malware. Ils sont implémentés par OAuthSD mais leur usage est découragé

Flux implicite (Implicit flow) - pour les applications basées sur un navigateur qui n’ont pas de backend, par exemple une application Javascript. Ce flux est également utilisé pour les applications à page unique (SinglePage Application, SPA). Le jeton d’identification est reçu directement avec la réponse de redirection de l’OP. Aucun appel au canal de retour n’est requis ici. De ce fait, le client ne peut être authentifié.

Flux hybride (Hybrid flow) - Essentiellement une combinaison du code et des flux implicites, rarement utilisé. Permet à l’application front-end et back-end de recevoir des jetons séparément l’une de l’autre.

OpenID Connect : Points d’extrémité

, par DnC

Les points d’extrémité d’OpenID Connect jouent un rôle semblable à ceux du protocole OAuth 2.0 mais sont plus complets.

Points d’extrémité définis par OpenID Connect

Le point d’extrémité d’autorisation et le point d’extrémité de jeton sont tous deux situés sur le serveur d’autorisation (ce serveur).

Le point d’extrémité de redirection est situé dans l’application cliente. Il revient donc à l’administrateur de cette application de développer le code du point d’extrémité de redirection.

La spécification d’OpenID Connect ne décrit pas comment l’URI de ces points d’extrémité est définie. C’est à chaque développeur de décider. Cependant, l’observation de différents serveurs permet de dégager des options courantes, que nous avons adoptées.

Notons que OpenID Connect prévoit la découverte des points d’extrémités, ce que OAuthSD implémente à cette URL : [http://oa.dnc.global/.well-known/openid-configuration]

Voici comment OAuthSD définit les points d’extrémité du protocole OpenID Connect :

Point d’extrémité d’autorisation (Authorization Endpoint)
https://oa.dnc.global/authorize
Usage : OpenID Connect : Obtenir une autorisation pour l’application cliente.

Le point d’extrémité d’autorisation est le point d’extrémité sur le serveur d’autorisation auquel l’agent de l’utilisateur final (user-agent : le navigateur ou l’application mobile etc.) est redirigé par l’application cliente, pour permettre à l’utilisateur final de s’authentifier et d’accorder les autorisations demandées par l’application cliente.

Lors du traitement de la demande d’authentification, le serveur crée un jeton d’accès et un jeton d’identification. Ces jetons seront associés à l’ID de l’utilisateur final, ce qui ouvre une notion de session.

Dans le cas du flux d’autorisation via un code (celui qui nous intéresse le plus), à ce stade, il n’est retourné qu’un code d’autorisation de durée de vie très courte [1]. Les jetons ne seront retournés qu’en réponse à l’appel du point d’extrémité token, dans une liaison serveur-serveur, ce qui fonde la sécurité de ce flux.

Dans le cas des flux implicites et hybrides, utilisés pour des applications sans serveur (back-end), les jetons sont directement retournés dans la réponse et sont donc visibles par l’agent de l’utilisateur, ce qui est considéré comme une faiblesse de ces flux.

Point d’extrémité de redirection, ou URI de retour à l’application cliente (Redirection Endpoint)
Le point d’extrémité de redirection est le point d’extrémité dans l’application cliente vers lequel l’agent de l’utilisateur est redirigé, après avoir obtenu l’autorisation au point d’extrémité d’autorisation. Cet URI est défini par l’administrateur d’une application cliente quand il l’inscrit sur ce serveur. Voir : OpenID Connect : Lier une application cliente au serveur OAuthSD. Ce point d’extrémité pourrait être distinct de celui du protocole OAuth 2.0 car OpenID Connect prévoit des échanges différents et requiert que le jeton obtenu soit vérifié par l’application cliente. Cet article explique comment faire : OpenID Connect : Exemples complets du flux d’Autorisation via un code puis requête UserInfo

Point d’extrémité de jeton (Token Endpoint)
https://oa.dnc.global/token
Usage : OpenID Connect : Obtenir les jetons d’accès

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 et l’ID client, pour obtenir un jeton d’accès et un jeton d’identité ou rafraîchir des jetons ayant expiré.

Le jeton d’accès est utilisé pour effectuer des appels authentifiés vers une API sécurisée, tandis que le jeton d’identification contient des attributs de profil utilisateur représentés sous la forme de déclarations (claims). Les deux JWT ont une date d’expiration indiquée par la revendication exp (parmi d’autres mesures de sécurité, comme la signature).

Point d’extrémité d’introspection (Introspection Endpoint)
https://oa.dnc.global/introspect
Usage : Open ID Connect : Introspection.

La "norme" OpenID Connect ne définit pas l’introspection. En effet, dans une approche simpliste, OpenID Connect ne protège que les données UserInfo qui, étant situées sur le même serveur, ne nécessitent pas l’introspection.
Mais si on souhaite utiliser OpenID Connect pour accéder à des données protégées situées sur un serveur de ressource étranger, l’introspection est une des deux méthodes applicables pour permettre à ce serveur de valider le jeton JWT. La problématique générale est exposée ici : Validation du jeton d’accès par une ressource protégée.

Notons que l’introspection n’est pas une particularité d’OAuthSD, d’autres implémentations offrant également cette fonctionnalité.

Remarque à discuter : Nous sommes d’avis que l’introspection affaiblit la sécurité d’une application, et que la meilleure méthode consiste à valider le jeton localement au serveur de ressource. Cependant, l’introspection peut être utile, en plus de la validation locale, pour obtenir des informations actualisées sur l’authentification.

Point d’extrémité d’informations sur l’utilisateur final (UserInfo)
https://oa.dnc.global/userinfo
Pour réaliser l’authentification, le protocole OpenID Connect permet à une application cliente d’accéder à l’identité de l’utilisateur final ayant accordé l’autorisation, ainsi qu’à différentes informations selon le scope passé dans la requête.
Usage :
- Demande d’informations sur l’utilisateur (UserInfo Endpoint),
- Réponse UserInfo.
Références :
- UserInfo Endpoint

Point d’extrémité d’informations sur les clefs (Keys Endpoint)
Ce point permet à un serveur de ressource d’accéder à la clé publique qui lui permettra de valider le jeton d’identité, comme exposé dans cet article : Validation du jeton d’accès par une ressource protégée.

https://oa.dnc.global/keys

Ce sujet est traité plus en détail ici : OpenId Connect : Point d’extrémité d’informations sur les clefs (Keys Endpoint)

Découverte des points d’extrémité
Conformément à la spécification OpenID Connect Discovery 1.0, OAuthSD expose ses métadonnées à l’URI :

https://oa.dnc.global/.well-known/openid-configuration

Ce sujet est traité plus en détail ici : OpenID Connect : Découverte.

Notes

[1Nous prévoyons de le rendre à usage unique. Il ne semble pas que la norme le prévoit, est-ce convenable ?

OpenID Connect : Lier une application cliente au serveur OAuthSD

, par DnC

Cet article s’adresse à un développeur.

Il décrit comment adapter une application pour qu’elle devienne cliente OpenID Connect et comment l’inscrire sur OAuth Server byDnC.

Ceci suppose que le développeur ou son organisation soit inscrit sur le serveur en tant qu’Administrateur d’applications.

Le processus comporte deux aspects :

- du côté du serveur OAuth, inscrire l’application,
- du côté de l’application cliente, insérer le code nécessaire pour assurer le lien avec le serveur OAuth.

1. S’inscrire en tant qu’Administrateur d’applications

L’inscription se fait ici : Formulaire d’inscription.

Renseignez soigneusement votre fiche d’Administrateur d’applications, la plupart de ces informations étant communiquées aux utilisateurs finaux dont vous devez gagner la confiance.

Attention : ne confondez pas l’inscription en tant qu’Administrateur d’applications (un développeur qui inscrit son application cliente pour qu’elle puisse déléguer l’authentification à OAuthSD) avec l’inscription d’un internaute en tant qu’utilisateur final des applications clientes, qui se fait ici : Je m’inscris.

Notes :
- Le terme "Administrateur d’applications" permet de distinguer le développeur ou le propriétaire d’une application cliente qui s’inscrit sur ce serveur, de l’utilisateur final, qui doit aussi s’inscrire, mais autrement. Dans le modèle de données sous-jacent, il s’agit également de l’objet éditorial de SPIP (la table auteurs), tandis que l’utilisateur final correspond à une table distincte.

2. Inscrire l’application cliente sur le serveur OAuthSD

Dans la rubrique Gérer, aller à Ajouter (Inscrire) une application cliente et remplir le formulaire :

- Client Id (obligatoire) : Chaine identifiant l’application de façon unique. Entrez un nom court, de préférence sans espace ni caractère spécial. Cet identifiant doit être unique pour tous les administrateurs d’applications inscrits sur le serveur. Il est visible du public et doit donc être représentatif de l’application et de votre entreprise.

- Client secret (obligatoire) : une courte chaîne assimilable à un mot de passe fort. Ce code doit rester secret. Il doit être fourni par l’application cliente lors de la demande de jeton.

- Redirect uri (obligatoire) : URI de retour à l’application cliente. C’est l’adresse à laquelle le serveur OAuth fait retour sur le client avec le résultat de l’authentification. OpenID Connect permet d’inscrire plusieurs URI de retour, cependant cette fonctionnalité n’a pas été testée dans l’état actuel du développement.

- Grant Type (obligatoire) : Sauf cas particulier, sélectionnez "Authorization Code".

- Scopes (obligatoire) : Liste des Scopes autorisés pour l’application, séparés par un espace. Le scope ’openid’ est obligatoire. Les scopes standard de OpenID Connect sont : profile, email, address, phone. Voyez la rubrique : Réponse UserInfo. Le scope ’offline_access’ sert à obtenir un jeton de rafraîchissement. En plus de ces scopes, des scopes particuliers peuvent être définis pour une application donnée. Exemple : "openid profile email administrateur".

- User id Si un nom d’utilisateur (username) est inscrit ici, seul cet utilisateur pourra se connecter, cet identifiant étant figé dans le formulaire d’authentification. Dans le cas général ce champ sera vide.

Vérifiez vos entrées et actionnez le bouton "Enregistrer".

Vous pourrez retrouver l’application et la modifier à la rubrique Toutes vos applications clientes.

Notes :
- OAuthSD crée pour l’application cliente une paire de clés publique/privée. Si vous souhaitez la changer, allez à la rubrique Toutes vos applications clientes et sélectionnez l’action "clés" correspondant à l’application.

3. Insérer dans l’application le code nécessaire

Bien que OpenID Connect soit construit sur OAuth 2.0, il ne faut pas s’attendre à réutiliser le code écrit pour ce protocole. En effet, OpenID Connect est une couche d’abstraction de niveau supérieur, impliquant des échanges et des points d’extrémité différents, ce qui nécessite que le code soit réécrit.

Si l’application cliente est conçue pour déléguer ses authentifications à un serveur à la norme OpenID Connect, il n’y a rien de plus à faire.

Sinon, c’est une affaire de développeur. Il y a deux adaptations à réaliser :

- Écrire le code situé au Point d’extrémité de redirection, ou URI de retour à l’application cliente (Redirection Endpoint). Il s’agit de demander au serveur OAuthSD un jeton d’accès et un jeton d’identification pour l’application, à partir du Code d’autorisation (Authorization code) retourné par le serveur.

- Écrire la procédure d’authentification adapté à la technique de l’application cliente. Généralement, ce sera un module d’authentification supplémentaire, ou une ré-écriture du code d’authentification. Voyez Plugin OpenID Connect Client pour SPIP.

Consommer une API protégée de type HTTP REST est extrêmement simple. Voici un exemple élémentaire, juste pour tester le fonctionnement, combinant en un seul script l’autorisation et la consommation de UserInfo :

PHP

  1. <?php
  2. /*
  3. testopenid2.php
  4.  
  5. Test de OpenID Connect
  6. Inscrivez sur ce serveur une application de test,
  7. Renseignez les constantes ci-dessous en conséquence,
  8. Lancez ce script : http://oa.dnc.global/oidc/testopenid2.php
  9.  
  10. Author :
  11. Bertrand Degoy https://degoy.com
  12. Credits :
  13. bschaffer https://github.com/bshaffer/oauth2-server-php
  14.  
  15. Licence : MIT licence
  16. Copyright (c) 2016 - DnC
  17.  
  18.  
  19. */
  20.  
  21. ini_set('display_errors', 1);
  22.  
  23. $client_id = 'testopenid';
  24. $client_secret = 'thesecret';
  25. //$redirect_uri = '';
  26.  
  27. $authorization_endpoint = 'http://oa.dnc.global/authorize';
  28. $token_endpoint = 'http://oa.dnc.global/token';
  29. $userinfo_endpoint = 'http://oa.dnc.global/userinfo';
  30.  
  31.  
  32. if (isset($_GET['error']))
  33. {
  34. exit("Error: {$_GET['error']}. Description: {$_GET['error_description']}");
  35. }
  36. else if (isset($_GET['code']) && isset($_GET['state']))
  37. {
  38. // Step 2. Token request
  39.  
  40. $code = $_GET['code'];
  41. echo "Authorization Code is {$code}\n\n";
  42.  
  43. $data = array(
  44. 'grant_type' => 'authorization_code',
  45. 'code' => $code,
  46. );
  47.  
  48. $h = curl_init($token_endpoint);
  49. curl_setopt($h, CURLOPT_RETURNTRANSFER, true);
  50. curl_setopt($h, CURLOPT_TIMEOUT, 10);
  51. curl_setopt($h, CURLOPT_USERPWD, "{$client_id}:{$client_secret}");
  52. curl_setopt($h, CURLOPT_POST, true);
  53. curl_setopt($h, CURLOPT_HTTPHEADER, array('Content-Type: application/x-www-form-urlencoded'));
  54. curl_setopt($h, CURLOPT_POSTFIELDS, http_build_query($data));
  55. //curl_setopt($h, CURLOPT_SSL_VERIFYPEER, false);
  56.  
  57. $res = curl_exec($h);
  58. if (!$res)
  59.  
  60. $res = json_decode($res, true);
  61.  
  62. echo "Token Response:\n";
  63. print_r($res);
  64. echo "\n";
  65.  
  66. // Here you should decode JWT token and check sign using server's public key
  67. // $payload = Jwt::decode($response['id_token'], $this->serverPublicKey);
  68.  
  69. // If Token Response is valid goto step 3
  70. // Step 3. Get UserInfo
  71. $access_token = $res['access_token'];
  72.  
  73. $h = curl_init($userinfo_endpoint);
  74. curl_setopt($h, CURLOPT_RETURNTRANSFER, true);
  75. curl_setopt($h, CURLOPT_TIMEOUT, 10);
  76. curl_setopt($h, CURLOPT_HTTPHEADER, array('Authorization: Bearer ' . $access_token));
  77. //curl_setopt($h, CURLOPT_SSL_VERIFYPEER, false);
  78.  
  79. $res = curl_exec($h);
  80. if (!$res)
  81.  
  82. $res = json_decode($res, true);
  83.  
  84. echo "UserInfo Response:\n";
  85. print_r($res);
  86. }
  87. else
  88. {
  89. // Step 1. Authorization Code request
  90.  
  91. $data = array(
  92. 'response_type' => 'code',
  93. 'client_id' => $client_id,
  94. 'state' => 'xyz',
  95. 'scope' => 'openid profile',
  96. );
  97.  
  98. $authorization_endpoint .= '?' . http_build_query($data);
  99. header('Location: ' . $authorization_endpoint);
  100. exit();
  101. }
  102. ?>

Télécharger

Exécuter le script : http://oa.dnc.global/oidc/tests/tes...

Une version rigoureuse et donc plus complète est donnée ici : OpenID Connect : Exemples complets du flux d’Autorisation via un code puis requête UserInfo.

Open ID Connect : Introspection

, par DnC

L’introspection permet à un serveur de ressource (RS) étranger à l’organisation gérant le serveur d’authentification (AS) de valider un jeton d’identité (ID Token).

Cela permet également de rafraîchir les informations sur le processus d’authentification à l’origine de la création du jeton.

Avantages et inconvénients de l’Introspection

La validation du jeton d’identité (ID Token) auprès du serveur d’authentification (introspection) présente trois avantages importants :
- elle permet de savoir si le jeton a été révoqué ;
- on peut obtenir des informations sur l’utilisateur final (qui est à l’origine de l’autorisation) qui permettent d’identifier cet utilisateur et, donc, d’agir en fonction de la confiance à accorder d’après son profil ;
- il est possible de mettre en œuvre la définition "jti" (JWT ID) qui permet au serveur d’autorisation de vérifier que le jeton n’a pas déjà été utilisé.

Elle a cependant l’inconvénient d’augmenter le trafic avec le serveur d’autorisation, ce qui peut être minimisé par une mise en cache des réponses du côté du RS.

Remarque quant au lien entre jeton d’accès et jeton d’identité

Justin Richer : "Il n’y a pas de relation 1:1 entre le jeton d’accès et jeton d’identité (ID Token), surtout si vous considérez que le jeton d’accès peut être actualisé ou ne pas expirer, alors que le jeton d’identité doit expirer. Le ID Token représente l’événement authn (et la session, dans une certaine mesure), alors que le jeton d’accès représente une autorisation déléguée d’accès aux informations de profil de l’utilisateur. Vous pouvez obtenir des informations sur l’événement d’authentification qui a généré le jeton d’accès à partir de l’introspection, mais il est important de se rappeler que le jeton d’accès n’est pas destiné à être étroitement lié à cet événement d’authentification. En fait, c’est toute la question d’OAuth qui lie de façon lâche l’authentification au jeton d’accès et maintient le jeton d’accès valide alors que l’utilisateur n’est plus là.

Donc vous n’avez pas besoin d’introspection pour mettre en œuvre OpenID Connect (OIDC), tout comme vous n’en avez pas besoin pour OAuth2, mais vous pouvez le mettre en œuvre comme une fonction complémentaire (assez utile)".

Implémentation de l’Introspection

Il n’y a pas (à ce jour) de "norme" définissant l’Introspection pour OpenID Connect. Cependant, les implémentations courantes se fondent sur la proposition de standard RFC 7662 : OAuth 2.0 Token Introspection.

Point d’extrémité d’introspection (Introspection Endpoint)

https://oa.dnc.global/introspect

Forme de la demande d’Introspection

Le jeton est passé avec le paramètre "token" par l’une des méthodes suivantes : Auth Header, GET ou POST.

Une seule méthode doit être utilisée à la fois, sinon le serveur retourne ’400, invalid_request’.

Méthode Auth Header :
C’est la méthode recommandée.

Méthodes POST :
Lorsque vous placez le jeton dans le corps de la requête HTTP, la méthode doit être POST ou PUT. Sinon, le serveur retourne 400, ’invalid_request’, ’When putting the token in the body, the method must be POST or PUT’, ’#section-2.2’.

Le type de contenu pour les requêtes POST doit être "application / x-www-form-urlencoded. Si ce n’est pas le cas, le serveur retourne 400, ’invalid_request’, ’The content type for POST requests must be "application/x-www-form-urlencoded".

Méthodes GET :
Cette méthode n’est pas recommandée pour des raisons de sécurité.

Réponse du serveur

En cas de succès, le serveur retourne une réponse HTTP 200.

Le corps de la réponse contient un tableau portant les informations suivantes :

index type valeur
status entier code HTTP
headers string Headers de la réponse
page string JSON Array :
active : true,
scope : (JSON string Array) scopes associés au jeton.
client_id : ID de l’application cliente qui a demandé ce jeton.
username : ID OAuth de l’utilisateur final (human-readable).
exp : (long) (secondes depuis le 1° janvier 1970). Unix Time de la fin de validité du jeton.
iat : (long) (secondes depuis le 1° janvier 1970). Unix Time de création du jeton
iss : (string) issuer : serveur d’authentification qui a diffusé ce jeton.
sub : identifiant interne de l’utilisateur qui a autorisé ce jeton.
aud : audience définie pour ce jeton.

 

Si le jeton n’est pas valide, alors que la requête n’a pas échoué, l’introspection ne retourne un code HTTP 200 et une réponse active : false.

Note :
- Le traitement des erreurs d’introspection décrit dans ce qui suit est propre à cette version de OAuthSD, destinée au développement et à la mise au point d’une application mettant en oeuvre la délégation d’authentification. Conformément à la spécification, les serveurs OAuthSD de production ne donnent pas de détail sur l’erreur, mais retournent simplement un code HTTP 200 et une réponse active : false.

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

index type valeur
page string 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_request Only one method may be used to authenticate at a time (Auth header, GET or POST) La requête est mal formée
400 invalid_request Missing parameters : "token" required La requête Introspection requiert le paramètre ’token’.
400 invalid_request When putting the token in the body, the method must be POST or PUT Si on place le token dans le corps de la requête, la méthode ne peut être que POST ou PUT
400 invalid_request The content type for POST requests must be "application/x-www-form-urlencoded l’IETF spécifie ce type de contenu. NB : tous les serveurs Web ne remplissent pas cette variable _SERVER voir http://tools.ietf.org/html/rfc6750#section-2.2
401 invalid_token JWT is malformed le jeton JWT ne peut être décodé.

Exemples

Demande de validation d’un jeton d’identité :

SPIP

  1. /*
  2. Autorisation avec OAuth Server by DnC
  3. OpenID Connect : Introspection
  4. Auteur : Bertrand degoy
  5. Copyright (c) 2016 DnC
  6. */
  7.  
  8. function oauth_authorize($idtoken) {
  9.  
  10. $Ok = false;
  11.  
  12. if ( !empty( $idtoken) ) {
  13.  
  14. // Interroger l'introspection de OAuth Server by DnC
  15. include_spip('inc/distant');
  16. $url = "http://oa.dnc.global/introspect?token=" . $idtoken;
  17. $response = recuperer_url($url);
  18.  
  19. if ( (int)$response['status'] === 200 ) {
  20. $jwt = json_decode($response['page'], true);
  21. if ( $jwt['active'] == 'true' ) {
  22. if ( isset($_SERVER["HTTP_ORIGIN"]) ) {
  23. // Accès HTTP (CORS) : autoriser l'origine
  24. include_spip('inc/headers');
  25. $issuer = trim(strtr($_SERVER["HTTP_ORIGIN"], '<>"\'', '[]##'));
  26. header('Access-Control-Allow-Origin', $issuer);
  27. }
  28. $Ok = true;
  29. }
  30. }
  31.  
  32. }
  33.  
  34. return $Ok;
  35.  
  36. }

Télécharger

Un exemple plus complet, faisant apparaître la totalité des erreurs possibles, figure ici : OpenID Connect : Exemples complets du flux d’Autorisation via un code puis requête UserInfo.

Notes :

- Dans le cas où l’application cliente et le serveur de données protégées se trouvent dans des domaines différents, il faut gérer l’autorisation HTTP, comme cela est fait dans l’exemple ci-dessus. Voyez Contrôle d’accès HTTP (CORS).

- L’interrogation du serveur d’autorisation à chaque accès d’une ressource protégée peut le surcharger. Pour éviter cela, il est recommandé de mettre en cache la réponse du serveur du côté du serveur de ressource. Avec SPIP, c’est le rôle de la fonction recuperer_url_cache() qui pourra remplacer recuperer_url() dans l’exemple précédent. La fonction permet de régler le délai de garde en cache, qu’il convient de fixer à une durée assez courte (10 secondes par exemple), l’essentiel étant de ne pas bombarder le serveur. Voici un exemple :

SPIP

  1. $res = recuperer_url_cache( $url, array('delai_cache' => 10) ); // Délai de 10s

La fonction décrite précédemment peut être utilisée dans une fonction d’autorisation d’accès à un objet SPIP ( ici l’objet gis de radar ) :

SPIP

  1. function _autoriser_gis($faire, $quoi, $id, $qui, $options) {
  2. if ( $qui['statut'] == '0minirezo' ) {
  3. // Toujours autoriser un administrateur
  4. return true;
  5. } else {
  6. if ( $idtoken = $_GET['token'] ) {
  7. // Vérifier le jeton d'accès
  8. return oauth_authorize($idtoken);
  9. } else return false;
  10. }
  11. }

Télécharger

OpenId Connect : Point d’extrémité d’informations sur les clefs (Keys Endpoint)

, par DnC

Le protocole OpenID Connect utilise un jeton d’identité (ID Token) fondé sur JWT. La norme décrit comment une application cliente doit valider un ID Token reçu en réponse à une demande d’authentification. Un tiers (le serveur de ressource protégé notamment) peut évidemment appliquer la même méthode, à condition d’accéder aux informations nécessaires. La validation est effectuée à l’aide d’un cryptage asymétrique, mettant en œuvre une paire de clé publique-privée. Le serveur de ressource protégé (RS) doit donc pouvoir accéder à la clé publique.

C’est ce que permet le

Point d’extrémité d’informations sur les clefs (Keys Endpoint)

https://oa.dnc.global/keys

Ce point permet à un serveur de ressource d’accéder à la clé publique qui lui permettra de valider le jeton d’identité. Les données au format JSON sont définies dans le document JSON Web Key (JWK).

Voici un exemple de réponse au format JSON :
[JSON]

{"keys":[{
"kid":"618584200ef916a154008d898a1e7edc",
"kty":"RSA",
"alg":"RS256",
"use":"sig",
"e":"AQAB",
"n":"ykcWIXjQ-f61XCJutT4JcgpmmobtB0U7ZcejT8tBD8rOZPkQDYf0Q3pMjCkNT8RRKzMYtkelY2CNn3U7kVJMgbJAtvZsCdlChVHAKvRnjwh1GR_6Zpmajm5cuz4bjQWWUIPIoXe_4JbC8nCrHdaagzB_6PrV_NILyn5unG1RLOrWx7_yzLaterDKxHTCBeOlqv_5VGFey0Ecf-X7Bj8YRx6fpamK4BcEAZSAbZMtAnTckp3hOYJgZo3MOXDxSQw1YR83i5Udcoaf7sxfhEA_b7r9CeNfgj76MKM7sdCfBMI7_JSz-YU_pJKCuT9Ny3IJQ0fQHpDzSq2oD_3cDcLjfXTGM67rXElwr9l8yrSNa29UGK4q2u9cFCQmJGlxVhZU6bzs7l4202LTJdPlzm_29jwLVvtqnVJSovMLHx84ReFtus1RdKRGB2plDQccvBNvp92D9lOnM3bAu1fKRAJwNh3hg1d6k7MVCHxoo9HVnkxzW48rAAJE2nk44a2Y0cclufBhvKRdNavldS1XOyZ_qf3qCAzsuYF1VAga8I-QOb6OyXp0KGLptbyYD-ZXISGPw3pDD3aAof_PMfFhSB96GHDnm-UCRpFHndQ_fZgtZhWugU8z22rV-irYCySqVkpE0ToWbNXNFZ9Jo1GXdwkpi1WjB7S-ipjzRFOlxhwbvZ0"},
...

Notes :
- OAuthSD permet de définir une paire de clé publique/privée pour chaque application cliente. Le point d’extrémité d’informations sur les clés peut donc être appelé avec le paramètre ’aud’ donnant l’identifiant de l’application cliente (client_id). Si ce paramètre n’est pas fourni, les informations retournées sont celles des clés par défaut, souvent dénommées "clés du serveur".

OpenID Connect : Découverte

, par DnC

Le protocole OpenID Connect nécessite l’utilisation de plusieurs points d’extrémité pour authentifier les utilisateurs et pour demander des ressources, y compris des jetons, des informations sur l’utilisateur et des clés publiques.

Comment un serveur de ressource indépendant du serveur d’autorisation peut-il connaître les fonctionnalités du serveur et y accéder ?

Le document de découverte

Conformément à la spécification OpenID Connect Discovery 1.0, OAuthSD expose ses métadonnées à l’URI :

https://oa.dnc.global/.well-known/openid-configuration

Voici un exemple (non stable) des métadonnées fournies au format JSON :

[JSON]

{
 "issuer"                                : "https://oa.dnc.global",
 "token_endpoint"                        : "https://oa.dnc.global/token",
 "introspection_endpoint"                : "https://oa.dnc.global/introspect",
 "revocation_endpoint"                   : "https://oa.dnc.global/revoke",
 "authorization_endpoint"                : "https://oa.dnc.global/authorize",
 "userinfo_endpoint"                     : "https://oa.dnc.global/userinfo",
 "jwks_uri"                              : "https://oa.dnc.global/keys",
 "scopes_supported"                      : [ "openid",
                                             "profile",
                                             "email",
                                             "address",
                                             "phone"],
 "response_types_supported"              : [ "code",
                                             "id_token",
                                             "token id_token",
                                             "id_token token",
                                             "code id_token" ,
                                             "code token id_token" ],
 "response_modes_supported"              : [ "query",
                                             "fragment",
                                              "form_post" ],
 "grant_types_supported"                 : [ "authorization_code",
                                             "refresh_token",
                                             "password",
                                             "client_credentials",
                                             "urn:ietf:params:oauth:grant-type:jwt-bearer"],      
 "code_challenge_methods_supported"      : [ "S256",
                                             "plain" ],
 "subject_types_supported"               : [ "public" ],                                
 "token_endpoint_auth_methods_supported" : [ "client_secret_basic",
                                             "client_secret_post",
                                             "client_secret_jwt",
                                             "private_key_jwt" ],                                
 "token_endpoint_auth_signing_alg_values_supported" :
                                           [ "HS256",
                                             "HS512",
                                             "HS384",
                                             "RS256",
                                             "RS384",
                                             "RS512"],                                
 "id_token_signing_alg_values_supported" : [ "RS256",
                                             "RS384",
                                             "RS512",
                                             "HS256",
                                             "HS384",
                                             "HS512" ],
 "userinfo_signing_alg_values_supported" : [ "RS256",
                                             "RS384",
                                             "RS512",
                                             "HS256",
                                             "HS384",
                                             "HS512" ],  
 "claim_types_supported"                 : [ "normal" ],  
 "claims_supported"                      : [ "sub",
                                             "iss",
                                             "auth_time",
                                             "acr",
                                             "name",
                                             "given_name",
                                             "family_name",
                                             "nickname",
                                             "email",
                                             "email_verified" ],
 "ui_locales_supported"                  : [ "fr" ],  
 "claims_parameter_supported"            : true,  
 "request_parameter_supported"           : false,
 "request_uri_parameter_supported"       : false,
 "require_request_uri_registration"      : false

Voir également :
- Rafraîchir un jeton d’accès
- OpenId Connect : Point d’extrémité d’informations sur les clefs (Keys Endpoint)

Un jeton d’identification peut être utilisé pour ...

, par DnC

Dans la litérature sur OpenID Connect, le point de vue est souvent limité à l’identification centralisée (SSO ou single sign-on).
Un jeton d’identification est la carte d’identité de l’utilisateur final : il peut être utilisé à d’autres fins que la connexion de base.

Sessions sans état - Le jeton d’identification peut être implémenté dans un cookie de navigateur. Cela supprime la nécessité de stocker des sessions côté serveur (en mémoire ou sur disque), ce qui peut être un fardeau pour la gestion et la montée en échelle. La session est vérifiée en validant le jeton d’identification. Lorsque le jeton atteint sa limite de validité (exp), l’application peut simplement en demander un nouveau à l’OP via une ré-authentification silencieuse prompt = none.
Cette utilisation du jeton est souvent associée à la gestion de session des applications à page unique (Single Page Application, SPA) [1].
PHP

  1. ...
  2.  
  3. // Tenter de récupérer l'user_id depuis le JWT du cookie
  4.  
  5. $GLOBALS['user_id'] = null;
  6. $prompt = 'login';
  7.  
  8. $decoded_jwt = unserialize($_COOKIE['decoded_jwt']);
  9.  
  10. if ( is_array($decoded_jwt)) {
  11.  
  12. if ( $decoded_jwt['exp'] > time() ) {
  13.  
  14. // Le JWT du cookie est valide et nous donne l'user_id
  15. $GLOBALS['user_id'] = $decoded_jwt['sub'];
  16.  
  17. } else {
  18. // Demander un nouveau jeton sans réauthentification (prompt=none)
  19. $prompt = 'none';
  20. }
  21. }
  22.  
  23. if ( is_null($GLOBALS['user_id']) ) {
  24. // sinon, il faut demander un jeton avec authentification de l'utilisateur
  25. ...

Télécharger

DnC a développé des applications de ce type. Par exemple, photos.dnc.global est une application sans base de données, dont les sessions sont fondées sur le JWT en cookie. On comprend l’intérêt pour la montée en échelle : une telle application peut être répartie sur de nombreux serveurs sans que le développeur ait à se soucier de réplication des données.

Echange de jetons - Le jeton ID peut être échangé contre un jeton d’accès au point de terminaison du jeton d’un serveur d’autorisations OAuth 2.0 ( cf. Draft IETF : Token Exchange ). Il existe des scénarios réels où un document d’identité est requis pour obtenir l’accès, par exemple lorsque vous vous enregistrez dans un hôtel pour obtenir la clé de votre chambre. L’échange de jetons a des utilisations dans les applications distribuées et d’entreprise.

Passage d’identité à des tiers - Le jeton ID peut être transmis à d’autres composants d’application ou à des services d’arrière-plan lorsque la connaissance de l’identité de l’utilisateur (end-user) est requise, par exemple pour identifier les enregistrements d’un historique.
Il est également possible à une application de passer le jeton pour obtenir l’accès à une ressource protégée, à charge pour celle-ci de le valider. C’est dans cette configuration que se situent les exemples que nous donnons ici : OpenID Connect : Exemples complets du flux d’Autorisation via un code puis requête UserInfo. Dans ce cas, il peut être intéressant de définir un scope particulier qui signifiera que l’application cliente peut accéder à la ressource considérée.

Notes

[1Remarquons que cette technique est particulièrementavantageuse pour gérer des applications multi-pages et/ou un ensemble d’applications à page unique.