i-TegoDocumentationAuthentification > OpenID Connect OAuth Serveur dédié > Développer > OpenID Connect

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

  publié le par i-Tego WM

OpenID Connect est une couche d’identité au dessus de l’architecture de délégation d’autorisation OAuth 2.0 permettant aux applications :
- de vérifier l’identité de l’usager et de la lier à une application et à une portée d’autorisation,
- d’obtenir 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 2.0 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 et l’application qui sont à l’origine de l’autorisation.
OAuth ne fournit pas cette identité et doit être complété pour assurer l’authentification.

OpenID Connect

OpenID Connect est une couche d’authentification construite sur OAuth 2.0. Le serveur d’autorisation (ou "fournisseur OpenID" dans ce cas, en anglais OpenID Provider ou OP) 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 de cette 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 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. Mais cela reste des options, le standard est ouvert.

OpenID Connect : Types d’autorisations (Grant Type)

  (publié initialement le jeudi 22 décembre 2016) par i-Tego WM

Les flux d’autorisation (Grant Type) spécifiquement définis pour le protocole OpenID Connect sont :
- Autorisation via un code (Authorization Code Grant),
- Autorisation implicite (Implicit Grant),
- Flux Hybride (Hybrid Flow).


Voyez également : OpenID Connect et OAuth 2.0 : Synthèse des flux d’autorisation (Grant Type).


Flux de code d’autorisation (spéc. : Authorisation code flow) - le flux le plus couramment utilisé, destiné aux applications Web traditionnelles ainsi qu’aux applications natives / mobiles (avec cependant des risques avec ces dernières ). 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.

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.

Le flux de code d’autorisation offre une sécurité optimale, car :
- le secret de l’application, résidant sur le serveur [1], est protégé,
- les jetons ne sont pas révélés au navigateur (ne circulent que dans une liaison serveur-serveur),
- l’application cliente peut être authentifiée, tout comme l’utilisateur final,
- la signature du jeton lie de façon indissoluble : l’identité de l’utilisateur final, l’identité de l’application, la portée (scope) de l’autorisation.

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

Flux implicite (spéc. : 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é.

La mise en œuvre de ce flux dans le cadre d’OAuthSD est détaillée ici : Autorisation implicite.

Flux Hybride (spéc. : Hybrid Flow) - Essentiellement une combinaison du code et des flux implicites, rarement utilisé. On peut lire : "Permet à l’application front-end et back-end de recevoir des jetons séparément l’une de l’autre". Il semble plus sécurisé et tout aussi facile d’appliquer séparément un flux de code et un flux implicite.

Les flux Implicite et Hybride sont implémentés par OAuthSD mais leur usage est découragé car ils ne présentent pas les avantages de sécurité attachés à OpenID Connect :
- ils compromettent le secret de l’application qui est facilement accessible au public,
- ils exposent les jetons au navigateur de l’utilisateur final, ce qui rend possible une exploitation par un malware,
- ils ne permettent pas d’authentifier l’application cliente.

Notes :
- Les fonctionnalités d’OAuth 2.0 sont intégrées au protocole OpenID Connect. Toutes les fonctionnalités d’OAuth 2.0 et de OpenID Connect peuvent donc être atteintes par les Points d’extrémité d’OpenID Connect, en particulier les flux Client/User Credentials d’OAuth 2.0. Tous les flux sont résumés ici : OpenID Connect et OAuth 2.0 : Synthèse des flux d’autorisation (Grant Type).
- Le flux Hybrid n’est que partiellement implémentée par OAuthSD. Cette méthode est peu utilisée et peut être avantageusement remplacée par une successsion d’appels aux deux premières.

OpenID Connect : Le jeton d’identité ID Token (JWT signé ou JWS)

  publié le par i-Tego WM

Dans le cadre d’OpenID Connect, le jeton d’identité ID_Token de type JWT signé (JWS), suit la définition générale du jeton JSON Web Token (JWT). Le jeton est signé, ce qui justifie l’acronyme JWS. Cependant, les déclarations de la charge utile définies pour OpenID Connect diffèrent sur certains points et quelques déclarations spécifiques complètent le format.

La spécification du jeton d’identité (ID_Token) pour OpenID Connect est donnée ici : OpenID Connect Core 1.0 incorporating errata set 1 :

2. Jeton d’identité

L’extension principale qu’OpenID Connect fait à OAuth 2.0 pour permettre aux utilisateurs finaux d’être authentifiés est la structure de données du jeton d’identité. Le jeton d’identité est un jeton de sécurité qui contient des déclarations concernant l’authentification d’un utilisateur final par un serveur d’autorisation lors de l’utilisation d’un client, et éventuellement d’autres déclarations demandées. Le jeton d’identité est représenté sous la forme d’un JSON Web Token (JWT) avec signature cryptographique (JWS).

Les déclarations suivantes sont utilisées dans le jeton ID pour tous les flux OAuth 2.0 utilisés par OpenID Connect :

iss
OBLIGATOIRE. Identifiant de l’émetteur pour l’émetteur de la réponse. La valeur iss est une URL sensible à la casse utilisant le schéma https qui contient des composants de schéma, d’hôte et, éventuellement, de numéro de port et de chemin d’accès et aucun composant de requête ou de fragment.
sub
OBLIGATOIRE. Identifiant du sujet. Un identifiant localement unique et jamais réaffecté au sein de l’émetteur pour l’utilisateur final, qui est destiné à être consommé par le client, par exemple, 24400320 ou AItOawmwtWwcT0k51BayewNvutrJUqsvl6qs7A4. Il NE DOIT PAS dépasser 255 caractères ASCII. La sous-valeur est une chaîne sensible à la casse.
aud
OBLIGATOIRE. Public (s) auquel ce jeton d’identité est destiné. Il DOIT contenir le client_id OAuth 2.0 de la partie utilisatrice comme valeur d’audience. Il PEUT également contenir des identifiants pour d’autres publics. Dans le cas général, la valeur aud est un tableau de chaînes sensibles à la casse. Dans le cas spécial courant où il n’y a qu’une audience, la valeur aud PEUT être une chaîne unique sensible à la casse.
exp
OBLIGATOIRE. Heure d’expiration à partir de laquelle le jeton d’identité NE DOIT PAS être accepté pour le traitement. Le traitement de ce paramètre nécessite que la date / heure actuelle DOIT être antérieure à la date / heure d’expiration répertoriée dans la valeur. Les implémenteurs PEUVENT prévoir une petite marge de manœuvre, généralement pas plus de quelques minutes, pour tenir compte du décalage d’horloge. Sa valeur est un nombre JSON représentant le nombre de secondes à partir de 1970-01-01T0 : 0 : 0Z mesuré en UTC jusqu’à la date / heure. Voir RFC 3339 [RFC3339] pour plus de détails concernant la date / heure en général et UTC en particulier.
iat
OBLIGATOIRE. Heure à laquelle le JWT a été émis. Sa valeur est un nombre JSON représentant le nombre de secondes à partir de 1970-01-01T0 : 0 : 0Z mesuré en UTC jusqu’à la date / heure.
auth_time
Heure à laquelle l’authentification de l’utilisateur final s’est produite. Sa valeur est un nombre JSON représentant le nombre de secondes à partir de 1970-01-01T0 : 0 : 0Z mesuré en UTC jusqu’à la date / heure. Lorsqu’une demande max_age est effectuée ou lorsque auth_time est demandé en tant que revendication essentielle, cette revendication est OBLIGATOIRE ; sinon, son inclusion est FACULTATIVE. (La revendication auth_time correspond sémantiquement au paramètre de réponse auth_time OpenID 2.0 PAPE [OpenID.PAPE].)
nonce
Valeur de chaîne utilisée pour associer une session client à un jeton d’identité et pour atténuer les attaques de relecture. La valeur est transmise sans modification de la demande d’authentification au jeton ID. S’il est présent dans le jeton d’identité, les clients DOIVENT vérifier que la valeur de réclamation nonce est égale à la valeur du paramètre nonce envoyé dans la demande d’authentification. S’ils sont présents dans la demande d’authentification, les serveurs d’autorisation DOIVENT inclure une réclamation nonce dans le jeton d’identité, la valeur de réclamation étant la valeur nonce envoyée dans la demande d’authentification. Les serveurs d’autorisation NE DEVRAIENT effectuer aucun autre traitement sur les valeurs nonce utilisées. La valeur nonce est une chaîne sensible à la casse.
acr
FACULTATIF. Référence de classe de contexte d’authentification. Chaîne spécifiant une valeur de référence de classe de contexte d’authentification qui identifie la classe de contexte d’authentification satisfaite par l’authentification. La valeur "0" indique que l’authentification de l’utilisateur final ne répond pas aux exigences du niveau 1. ISO / IEC 29115 [ISO29115]. L’authentification à l’aide d’un cookie de navigateur de longue durée, par exemple, est un exemple où l’utilisation du "niveau 0" est approprié. Les authentifications de niveau 0 NE DEVRAIENT PAS être utilisées pour autoriser l’accès à toute ressource de quelque valeur monétaire que ce soit. (Cela correspond au PAPIER OpenID 2.0 [OpenID.PAPE] nist_auth_level 0.) Un URI absolu ou un nom enregistré RFC 6711 [RFC6711] DEVRAIT être utilisé comme valeur acr ; les noms enregistrés NE DOIVENT PAS être utilisés avec une signification différente de celle qui est enregistrée. Les parties utilisant cette allégation devront s’entendre sur la signification des valeurs utilisées, qui peuvent être spécifiques au contexte. La valeur acr est une chaîne sensible à la casse.
amr
FACULTATIF. Références aux méthodes d’authentification. Tableau JSON de chaînes qui sont des identifiants pour les méthodes d’authentification utilisées dans l’authentification. Par exemple, des valeurs peuvent indiquer que des méthodes d’authentification par mot de passe et OTP ont été utilisées. La définition de valeurs particulières à utiliser dans la revendication amr dépasse le cadre de cette spécification. Les parties utilisant cette allégation devront s’entendre sur la signification des valeurs utilisées, qui peuvent être spécifiques au contexte. La valeur amr est un tableau de chaînes sensibles à la casse.
azp
FACULTATIF. Partie autorisée - la partie à laquelle le jeton d’identité a été délivré. S’il est présent, il DOIT contenir l’ID client OAuth 2.0 de cette partie. Cette déclaration n’est nécessaire que lorsque le jeton d’identification a une seule valeur d’audience et que cette audience est différente de la partie autorisée. Il PEUT être inclus même lorsque la partie autorisée est la même que le seul public. La valeur azp est une chaîne sensible à la casse contenant une valeur StringOrURI.

Les jetons d’identification PEUVENT contenir d’autres déclarations. Toute déclaration utilisée qui n’est pas comprise DOIT être ignorée. Voir les sections 3.1.3.6, 3.3.2.11, 5.1 et 7.4 pour les déclarations supplémentaires définies par cette spécification.

Les jetons d’identification DOIVENT être signés à l’aide de JWS [JWS] et éventuellement signés puis chiffrés à l’aide de JWS [JWS] et JWE [JWE] respectivement, fournissant ainsi l’authentification, l’intégrité, la non-répudiation et, éventuellement, la confidentialité, conformément à la section 16.14. Si le jeton d’identification est chiffré, il DOIT être signé puis chiffré, le résultat étant un JWT imbriqué, comme défini dans JWT.

Les jetons d’identification NE DOIVENT PAS utiliser ’none’ comme valeur alg, sauf si le type de réponse utilisé ne renvoie aucun jeton d’identification du point de terminaison d’autorisation (comme lors de l’utilisation du flux de code d’autorisation) et si le client a explicitement demandé l’utilisation de ’none’ au moment de l’enregistrement.

Les jetons d’identification NE DEVRAIENT PAS utiliser dans le champ d’entête les paramètres JWS ou JWE x5u, x5c, jku ou jwk. Au lieu de cela, les références aux clés utilisées sont communiquées à l’avance à l’aide des paramètres de découverte et d’enregistrement, conformément à la section 10.

Voici un exemple non normatif de l’ensemble de revendications (l’ensemble de déclarations JWT) dans un jeton d’identification :

{
  "iss": "https://server.example.com",
  "sub": "24400320",
  "aud": "s6BhdRkqt3",
  "nonce": "n-0S6_WzA2Mj",
  "exp": 1311281970,
  "iat": 1311280970,
  "auth_time": 1311280969,
  "acr": "urn:mace:incommon:iap:silver"
 }

Audience

Cette déclaration est interprétée différemment selon les OpenID Connect Provider (OP).

Rappelons que le jeton d’identité (ID Token) est un "jeton au porteur" (Bearer Token).
La plupart des systèmes utilisent donc la déclaration ’audience’ pour définir les applications qui peuvent légitimement présenter le jeton sous la forme :

"https://app-one.com", "https://app-two.com".

Il faut observer que les ressources protégées qui présentent le jeton à l’introspection peuvent également être mentionnées dans la liste des applications autorisées.

On voit que la déclaration audience appartient à ces définitions propres à un domaine particulier, dans lequel les applications et le serveur OpenID Connect Provider (OP) partagent la même interprétation.

C’est bien dans ce cas que se trouve OAuthSD qui est conçu et distribué comme OP privé.

Validation du jeton d’identité ID Token (JWT signé ou JWS)

  publié le par i-Tego WM

Les Jetons d’identité ne doivent jamais être approuvés tels quels. Les jetons peuvent être réutilisés par un malware, interceptés ou falsifiés par des attaquants. Lorsqu’une application ou une ressource protégée reçoit un jeton d’Identité JWT, elle doit toujours le valider.
Cependant, valider le jeton n’est pas tout : il faut encore vérifier qu’il est présenté par une application qui le détient légitimement.

Problématique

Le jeton d’identité, de type JWT signé (JWS), doit être validé dans deux situations :

- Dès sa réception, conformément à ce qui est défini dans la spécification d’OpenID Connect [1].

- Pour autoriser l’accès à une ressource protégée. Voyez comment le problème se pose de façon générale : Validation du jeton par une ressource protégée.
Deux cas se présentent :
- soit on passe la clé publique au serveur de ressource protégée RS qui procède localement à sa validation ; si ce RS n’est pas lié à l’organisation qui contrôle le serveur d’authentification, il peut utiliser la fonction API OpenID Connect : Découverte,
- soit on utilise une méthode dite "introspection" consistant à demander l’authentification du jeton JWT au serveur d’authentification qui l’a délivré : API Open ID Connect : Introspection (Introspection Endpoint) .

Procédure pour la Validation et la Consommation du jeton JWT signé (JWS)

La validation d’un jeton d’identité nécessite plusieurs étapes. Que le jeton soit validé du côté du serveur d’authentification ou à distance, la méthode est la même.

Le décodage et la validation du jeton suivent la méthode définie ici : Spécification OpenID Connect : Validation du jeton d’identité :

Les clients DOIVENT valider le jeton ID dans la réponse au jeton de la manière suivante :

- Si le jeton d’identification est chiffré, déchiffrez-le à l’aide des clés et des algorithmes spécifiés lors de l’enregistrement par le client, que l’OP devait utiliser pour chiffrer le jeton d’identification. Si le chiffrement a été négocié avec l’OP au moment de l’enregistrement et que le jeton d’identification n’est pas chiffré, le RP [2] DEVRAIT le rejeter.
- L’identifiant de l’émetteur pour le fournisseur OpenID (qui est généralement obtenu lors de la découverte) DOIT correspondre exactement à la valeur de la déclaration iss (issuer).
- Le client DOIT valider que la déclaration aud (audience) contienne la valeur client_id enregistrée auprès de l’émetteur identifié par la déclaration iss (émetteur) en tant qu’audience. La déclaration aud (audience) PEUT contenir un tableau avec plus d’un élément. Le jeton ID DOIT être rejeté si le jeton ID ne répertorie pas le client en tant qu’audience valide, ou s’il contient des audiences supplémentaires non approuvées par le client.
- Si le jeton d’identification contient plusieurs audiences, le client DEVRAIT vérifier qu’une déclaration azp est présente.
- Si une déclaration azp (partie autorisée) est présente, le client DEVRAIT vérifier que son client_id est la valeur de la déclaration.
- Si le jeton ID est reçu via une communication directe entre le client et le point d’extrémité du jeton (qui se trouve dans ce flux), la validation TLS du serveur PEUT être utilisée pour valider l’émetteur au lieu de vérifier la signature du jeton [3]. Le client DOIT valider la signature de tous les autres jetons ID conformément à JWS [JWS] en utilisant l’algorithme spécifié dans le paramètre d’en-tête JWT alg. Le client DOIT utiliser les clés fournies par l’émetteur.
- La valeur alg DEVRAIT être la valeur par défaut de RS256 ou l’algorithme envoyé par le client dans le paramètre id_token_signed_response_alg lors de l’enregistrement.
- Si le paramètre d’en-tête JWT alg utilise un algorithme basé sur MAC, tel que HS256, HS384 ou HS512, les octets de la représentation UTF-8 du secret client correspondant à l’identifiant client contenu dans la déclaration aud (audience) sont utilisés comme clé de validation de la signature. Pour les algorithmes basés sur MAC, le comportement n’est pas spécifié si l’aud est multivalué ou si une valeur azp est différente de la valeur aud.
- L’heure actuelle DOIT être antérieure à l’heure représentée par la déclaration exp.
- La déclaration Iat peut être utilisé pour rejeter des jetons qui ont été émis trop loin de l’heure actuelle, limitant ainsi la durée de stockage des clés pour prévenir les attaques. La plage acceptable est spécifique au client.
- Si une valeur nonce a été envoyée dans la demande d’authentification, une déclaration de nonce DOIT être présente et sa valeur vérifiée pour contrôler qu’il s’agit de la même valeur que celle qui a été envoyée dans la demande d’authentification. Le client DEVRAIT vérifier la valeur de nonce pour les attaques par relecture. La méthode précise pour détecter les attaques par relecture est spécifique au client.
- Si la déclaration acr a été mentionnée, le client DEVRAIT vérifier que la valeur de réclamation revendiquée est appropriée. La signification et le traitement des déclarations acr sont hors du domaine d’application de la présente spécification.
- Si la déclaration auth_time a été mentionnée, par le biais d’une demande spécifique pour cette déclaration ou du paramètre max_age, le client DEVRAIT vérifier la valeur de la déclaration auth_time et demander une nouvelle authentification s’il détermine qu’il s’est écoulé trop de temps depuis la dernière authentification de l’utilisateur final.

Exemple de code pour la vérification locale de la signature du jeton d’identité JWT signé (JWS)

La plupart de ces vérifications ne nécessitent qu’une simple comparaison de chaînes. La validation de la signature est plus complexe. L’implémentation qui en est faite par OAuthSD est décrite maintenant. La fonction suivante, tirée de OAuth 2.0 Server PHP, sépare les composantes du jeton, détecte les erreurs de format, vérifie éventuellement la signature et retourne la charge utile ou false en cas d’erreur.

La variable $key passe la clé publique qui a servi à générer le jeton JWT.

Si $key = null, la vérification de la signature n’est pas effectuée (on suppose le jeton déjà validé par introspection), et le contenu de la charge utile est retourné sous la forme d’un tableau associatif.

PHP

  1. /**
  2. * @author    Brent Shaffer <bshafs at gmail dot com>
  3.  * @license   MIT License
  4. */
  5.  
  6. /**
  7.     * Sépare les composantes du jeton, détecte les erreurs de format, vérifie la signature et retourne la charge utile ou false en cas d'erreur.
  8.     *
  9.     * @param mixed $jwt : le jeton JWT
  10.     * @param mixed $key : la clé publique
  11.     * @param mixed $allowedAlgorithms : un array des codes d'algorithmes autorisés (sous ensemble de HS256, HS384 ou HS512, RS256, RS384 et RS512). Si ce paramètre est précisé, le jeton doit indiquer l'algorithme et celui-ci doit être compris dans l'array.
  12.     * @param mixed return : charge utile (tableau associatif) ou false.
  13.     */
  14.  
  15. function decode($jwt, $key = null, $allowedAlgorithms = true)
  16.     {
  17.         if (!strpos($jwt, '.')) {
  18.             return false;
  19.         }
  20.  
  21.         $tks = explode('.', $jwt);
  22.  
  23.         if (count($tks) != 3) {
  24.             return false;
  25.         }
  26.  
  27.         list($headb64, $payloadb64, $cryptob64) = $tks;
  28.  
  29.         if (null === ($header = json_decode($this->urlSafeB64Decode($headb64), true))) {
  30.             return false;
  31.         }
  32.  
  33.         if (null === $payload = json_decode($this->urlSafeB64Decode($payloadb64), true)) {
  34.             return false;
  35.         }
  36.  
  37.         $sig = $this->urlSafeB64Decode($cryptob64);
  38.  
  39.         if ((bool) $allowedAlgorithms) {
  40.             if (!isset($header['alg'])) {
  41.                 return false;
  42.             }
  43.  
  44.             // check if bool arg supplied here to maintain BC
  45.             if (is_array($allowedAlgorithms) && !in_array($header['alg'], $allowedAlgorithms)) {
  46.                 return false;
  47.             }
  48.  
  49.             if (!$this->verifySignature($sig, "$headb64.$payloadb64", $key, $header['alg'])) {
  50.                 return false;
  51.             }
  52.         }
  53.  
  54.         return $payload;
  55.     }
  56.  
  57. function verifySignature($signature, $input, $key, $algo = 'HS256')
  58.     {
  59.         // use constants when possible, for HipHop support
  60.         switch ($algo) {
  61.             case'HS256':
  62.             case'HS384':
  63.             case'HS512':
  64.                 return $this->hash_equals(
  65.                     $this->sign($input, $key, $algo),
  66.                     $signature
  67.                 );
  68.  
  69.             case 'RS256':
  70.                 return openssl_verify($input, $signature, $key, defined('OPENSSL_ALGO_SHA256') ? OPENSSL_ALGO_SHA256 : 'sha256')  === 1;
  71.  
  72.             case 'RS384':
  73.                 return @openssl_verify($input, $signature, $key, defined('OPENSSL_ALGO_SHA384') ? OPENSSL_ALGO_SHA384 : 'sha384') === 1;
  74.  
  75.             case 'RS512':
  76.                 return @openssl_verify($input, $signature, $key, defined('OPENSSL_ALGO_SHA512') ? OPENSSL_ALGO_SHA512 : 'sha512') === 1;
  77.  
  78.             default:
  79.                 throw new \InvalidArgumentException("Unsupported or invalid signing algorithm.");
  80.         }
  81.     }

Télécharger

Avertissement à propos du paramètre ’alg’

La spécification prévoit d’appliquer la valeur du paramètre ’alg’ pour le choix de l’algorithme de validation de la signature : RFC 7515, section 4.1.1. : "... This Header Parameter MUST be present and MUST be understood and processed by implementations ...".

C’est une faille de sécurité, et donc une erreur de la spécification. OAuthSD applique la méthode définie pour chaque application, quelle que soit la valeur de alg. Il est intéressant de constater que OAuthSD passe les tests de validation de l’OIDF comme cela.

En savoir plus sur la validation du JWT :

- API Open ID Connect : Introspection (Introspection Endpoint)
- API OpenId Connect : Point d’extrémité d’informations sur les clefs (Keys Endpoint)
- OpenID Connect : Exemples complets du flux d’Autorisation via un code puis requête UserInfo

Voyez également :
- Validation du jeton d’accès avec la déclaration at_hash du jeton d’identité.

Notons que la validation du jeton ne suffit pas au serveur de ressource pour s’assurer que l’application qui présente le jeton le détient légitimement et éviter de répondre à une application étrangère. Voir à ce sujet :
- Vérification de l’origine de la requête reçue par un serveur de ressource.

Il existe des informations complémentaires, connectez vous pour les voir.

Notes

[1Voici pourtant ce que dit Google : "Normalement, il est essentiel de valider un jeton d’identification avant de l’utiliser, mais puisque vous communiquez directement avec Google via un canal HTTPS sans intermédiaire et que vous utilisez le secret de votre client pour vous authentifier auprès de Google, vous pouvez être sûr que le jeton que vous recevez vient vraiment de Google et est valide." C’est à dire que le jeton peut être n’importe quoi, une tartine, un cafard ou un cheval, du moment que l’on a une réponse c’est bon !!! Autant dire que le jeton ne sert à rien. Tiens, on reconnait là l’erreur qui avait prévalu avec le jeton d’accès d’OAuth 2.0. Heureusement il y a la suite : "Si votre serveur transmet le jeton d’identification à d’autres composants de votre application, il est extrêmement important que les autres composants le valident avant de l’utiliser. " (https://developers.google.com/ident...).

[2Relying Party : l’application cliente ou le serveur de ressource protégée etc..

[3Autrement dit, si la liaison entre le client et le serveur est sécurisée par TLS, on pourrait se passer de valider la signature. C’est ce que dit Google ici : https://developers.google.com/ident... . Cependant, nous considérons qu’il faut toujours valider la signature du jeton quel que soit l’utilisation, et pas seulement dans le cas où le jeton est retransmis à une application ou ressource tierce.

OpenID Connect : Lier une application cliente au serveur OAuthSD

  publié le par i-Tego WM

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’Auteur et 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’Auteur et Administrateur d’applications

L’inscription se fait ici : Formulaire d’inscription en tant qu’auteur et administrateur d’applications.

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, sans espace ni caractère spécial autre que ’_’. 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 au moins "Authorization Code". Pour les flux Hybrid, sélectionnez "Authorization Code" et "Implicit". Voyez OpenID Connect et OAuth 2.0 : Synthèse des flux d’autorisation (Grant Type).

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

Enfin, naviguez à l’adresse https://oa.dnc.global/keys afin de créer l’entrée correspondant à la nouvelle paire de clés publique/privée.

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. En effet, OAuthSD est 100% compatible OIDC ; les fonctionnalités supplémentaires sont apportées de façon transparente.

Si l’applicatif sur lequel est construit l’application cliente propose un plugin OpenID Connect, il suffira de l’installer. Nous proposons quelques plugins ici : Adaptation des applications.

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ée à 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, Extension OpenID Connect pour phpBB, Extension pour Wordpress "OpenID Connect Generic Client".

De façon optionnelle, on pourra également :
- Afficher l’état de la session OIDC et la gérer (Monitoring).

Consommer une API protégée de type HTTP REST : Un cas simple

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’authentification de l’utilisateur final 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 : GPL v3.0
  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 = 'https://oa.dnc.global/authorize';
  28. $token_endpoint = 'https://oa.dnc.global/token';
  29. $userinfo_endpoint = 'https://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.         exit(curl_error($h));
  60.  
  61.     curl_close($h);
  62.     $res = json_decode($res, true);
  63.  
  64.     echo "Token Response:\n";
  65.     print_r($res);
  66.     echo "\n";
  67.  
  68.     // Here you should decode JWT token and check signature using server's public key
  69.     // $payload = Jwt::decode($response['id_token'], $this->serverPublicKey);
  70.  
  71.     // If Token Response is valid goto step 3
  72.     // Step 3. Get UserInfo
  73.     $access_token = $res['access_token'];
  74.  
  75.     $h = curl_init($userinfo_endpoint);
  76.     curl_setopt($h, CURLOPT_RETURNTRANSFER, true);
  77.     curl_setopt($h, CURLOPT_TIMEOUT, 10);
  78.     curl_setopt($h, CURLOPT_HTTPHEADER, array('Authorization: Bearer ' . $access_token));
  79.     //curl_setopt($h, CURLOPT_SSL_VERIFYPEER, false);
  80.  
  81.     $res = curl_exec($h);
  82.     if (!$res)
  83.         exit(curl_error($h));
  84.  
  85.     curl_close($h);
  86.     $res = json_decode($res, true);
  87.  
  88.     echo "UserInfo Response:\n";
  89.     print_r($res);
  90. }
  91. else
  92. {
  93.     // Step 1. Authorization Code request
  94.  
  95.     $data = array(
  96.         'response_type' => 'code',
  97.         'client_id' => $client_id,
  98.         'state' => 'xyz',
  99.         'scope' => 'openid profile',    
  100.     );
  101.  
  102.     $authorization_endpoint .= '?' . http_build_query($data);
  103.     header('Location: ' . $authorization_endpoint);
  104.     exit();
  105. }
  106. ?>

Télécharger

Exécuter le script : https://oa.dnc.global/oidc/tests/te...

Sur le serveur OAuthSD, vous pouvez vous identifier avec login = bebert et password = 012345678

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.