i-TegoDocumentationAuthentification > OpenID Connect OAuth Serveur dédié > Développer > JSON Web Token (JWT, JWS)

Lorsque l’on utilise les flux du protocole OpenID Connect, une application peut obtenir un jeton d’identité (ID Token) au format JWT signé (JWS), en parallèle avec le jeton opaque normal.

Signé cryptographiquement, le JWT lie l’identité de l’application, celle de l’utilisateur final et les portées d’autorisation.

Après avoir décrit le jeton JWT, nous indiquons comment vérifier sa signature et le consommer.

Voyez ici ce que l’on peut faire avec : Un jeton d’identité peut être utilisé pour ....

JSON Web Token (JWT)

  (publié initialement le jeudi 5 janvier 2017) par i-Tego WM

JWT est un standard ouvert (RFC 7519) qui définit une manière compacte et autonome de transmission sécurisée d’informations entre les parties sous la forme d’un objet JSON.

Les documents suivants spécifient comment le jeton JWT est mis en oeuvre :
- pour OAuth 2.0 : RFC 7523 : JSON Web Token (JWT) Profile for OAuth 2.0 Client Authentication and Authorization Grants
- pour OpenID Connect : OpenID Connect Core 1.0 incorporating errata set 1.

 

Avertissement : OpenID Connect fait un usage particulier de JWT pour élaborer le jeton d’identité. Voyez : OpenID Connect : Le jeton d’identité ID Token (JWT signé ou JWS).

Structure du Jeton JWS (JWT signé)

Un jeton JWT (prononcé « jot ») signé se compose de trois parties séparées par des points (.) :

<header>.<payload>.<signature>

Chacune de ces trois composantes est codé Base64Url. OAuth 2.0 Server PHP utilise les fonctions suivantes :

PHP

  1. /**
  2. * @author    Brent Shaffer <bshafs at gmail dot com>
  3.  * @license   MIT License
  4. */
  5.  
  6.     public function urlSafeB64Encode($data)
  7.     {
  8.         $b64 = base64_encode($data);
  9.         $b64 = str_replace(array('+', '/', "\r", "\n", '='),
  10.                 array('-', '_'),
  11.                 $b64);
  12.  
  13.         return $b64;
  14.     }
  15.  
  16.     public function urlSafeB64Decode($b64)
  17.     {
  18.         $b64 = str_replace(array('-', '_'),
  19.                 array('+', '/'),
  20.                 $b64);
  21.  
  22.         return base64_decode($b64);
  23.     }

Télécharger

Bien que son format compact incite à le transmettre par URL, il est recommandé de le transmettre par Header dans l’en-tête Authorization avec le mécanisme d’authentification Bearer :

Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VybmFtZSI6ImRlbW8iLCJmdWxsTmFtZSI6

Header : Entête

L’en-tête se compose généralement d’un array JSON de deux membres :

Alg : le type du jeton, qui est donc JWT
Typ : l’algorithme de hachage utilisé, comme HMAC SHA256 ou RSA.

Pour coder/décoder la signature, OAuth Server by DnC utilise par défaut l’algorithme RS256 et peut utiliser HS256, HS384 ou HS512 ainsi que RS384 et RS512.

Par exemple :

{
 "typ": "JWT",
  "alg": "RS256"
}

Paylod : Charge utile

La charge utile contient les déclarations sous le format JSON.

Les déclarations (claims) sont des informations sur une entité (généralement, l’utilisateur) et des métadonnées supplémentaires.

Il existe trois types de déclarations : reserved, public et private.

Déclarations réservées

Ces déclarations sont réservées, en ce sens que leur nom ne doit pas être utilisé par d’autres déclarations qui pourraient être définies dans le cadre d’un protocole ou d’une application particuliers.

Elles sont prédéfinies par le standard JWT (voir Registered Claim Names).

Format des déclarations réservées

Avertissement : les définitions données ici sont celles de l’IETF (RFC 7523), différente de la définition générale donnée par la RFC 7519. La fondation OpenID (openid.net) donne une définition plus complète et différente sur certains points.

Les déclarations réservées propres à JWT sont les suivantes (voir JWT Format and Processing Requirements) :

1. Le jeton JWT DOIT contenir une déclaration "iss" (émetteur) contenant l’identifiant unique de l’entité qui a émis le jeton.

2. Le jeton JWT DOIT contenir une déclaration "sub" (subject) identifiant le Principal qui est le sujet de la JWT. Deux cas doivent être différenciés :

A. Pour Authorization Grant, le sujet identifie généralement un accesseur autorisé pour lequel le jeton d’accès est demandé (c’est-à-dire le propriétaire de la ressource ou un délégué autorisé), mais dans certains cas, ce peut être un identifiant pseudonyme ou une autre valeur indiquant un utilisateur anonyme. [1]

B. Pour Client Authentication [2], le sujet DOIT être le "client_id" du client OAuth.

3. Le jeton JWT DOIT contenir une définition "aud" (audience), audience(s) pour laquelle (ou lesquelles) ce jeton d’identité est destiné. Il DOIT contenir le client_id OAuth 2.0 de la partie de confiance en tant que valeur d’audience. [3]

4. Le jeton JWT DOIT contenir une définition "exp" (expiration time) qui limite la fenêtre de temps pendant laquelle le JWT peut être utilisé.

5. Le JWT PEUT contenir une définition « nbf » (not before) qui identifie l’instant avant lequel le jeton NE DOIT PAS être accepté.

6. Le JWT PEUT contenir une définition "iat" (issued at) qui identifie l’instant auquel le jeton a été délivré.

7. Le jeton JWT PEUT contenir une définition "jti" (JWT ID) qui fournit un identifiant unique pour le jeton. Si c’est le cas, le serveur OAuthSD s’assure qu’un jeton JWT n’est pas réutilisé pendant sa durée de validité.

Le jeton JWT peut également présenter une déclaration at_hash, voir : Validation du jeton d’accès avec la déclaration at_hash du jeton d’identité.

Déclarations publiques

Elles peuvent être définies à volonté. Mais pour éviter les collisions, leurs noms devraient être choisis dans le IANA JSON Web Token Registry mentionné précédemment ou être définis avec la déclaration d’un espace de noms.

Exemple de déclarations :

{  
 "iss": "https:\/\/oa.dnc.global",
 "iat":1410255630,
 "exp":1410259230,
 "sub": "a394190a00f2d36b21309036b0b",
 "aud":"_c036b0b6f97a394190a00f2d36b213091938d225"
}

Déclarations privées

Ce sont des déclarations personnalisées créées pour partager des informations entre les parties qui ont prévu de les utiliser.

Notes :
- Il doit être bien compris que l’en-tête et la charge utile sont de simples chaînes codées en base64, qui peuvent facilement être déchiffrées et lues. Il ne doit donc pas y figurer de donnée sensible.

Signature

La signature est utilisée pour vérifier que l’expéditeur du jeton JWT est bien celui qu’il prétend être et de veiller à ce que le message n’a pas été modifié en cours de route. Un JWT signé est conforme au standard JWS (JSON Web Signature, RFC 7515).

Le JWT est signé en utilisant un secret (avec l’algorithme HMAC) ou une paire de clés publique/privée en utilisant RSA. C’est cette deuxième solution qui est préférée par OAuthSD (la signature par HMAC est jugée insuffisamment sécurisée).

Lorsque l’application passe le jeton à un serveur de ressource protégée (RS), il est impératif que le serveur de ressource valide le jeton. Le serveur de ressource (RS) qui valide la signature doit être en possession de la clé publique correspondant à la clé privée avec laquelle la signature a été élaborée. Il est également possible, voire avantageux, de faire valider le jeton par le serveur d’authentification (voir : API Open ID Connect : Introspection (Introspection Endpoint)).

OAuthSD : gestion de la paire de clés publique/privée

A chaque application cliente enregistrée sur le serveur correspond une paire de clés publique/privée.

OAuthSD facilite la création et la gestion des clés de la façon suivante :

- La paire de clés publique/privée est créée automatiquement au moment de l’inscription de l’application cliente sur le serveur OAuthSD.

- L’interface de gestion d’OAuthSD permet à l’auteur-propriétaire de l’application d’éditer ces clés.

- De plus, un script updatekeys.php du core permet de re-générer les clés. Il est recommandé d’appeler ce script par une tâche CRON locale régulièrement, par exemple tous les deux mois.
Ce processus est totalement transparent pour les applications clientes validant la signature du JWT par introspection ou qui interogent le point d’accès Keys pour récupérer la clé publique.

Remarque de sécurité

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.

Notes

[1Cette définition approximative implique que la valeur de "sub" et son interprétation dépendent de l’application.

[2Il s’agit du flux JWT Bearer Authorization Grant.

[3La spécification prévoit : "Il PEUT aussi contenir des identifiants pour d’autres audiences. Dans le cas général, la valeur aud est un tableau de chaînes sensibles à la casse. Dans le cas particulier où il n’y a qu’une audience, la valeur aud PEUT être une chaîne sensible à la casse". La bibliothèque oauth2-server-php réduit "aud" à l’id du client client_id.

Emettre un jeton d’accès en tant que JWT

  publié le par i-Tego WM

La bibliothèque oauth2-server-php de Brent Shaffer permet d’émettre un jeton d’accès en tant que JWT.
Avec cette fonctionnalité, les flux de codes d’autorisation OAuth 2.0 et OpenID Connect semblent très similaires, cependant ...

Voyez : https://bshaffer.github.io/oauth2-server-php-docs/overview/jwt-access-tokens/

Configuration du serveur d’autorisations pour le jeton d’accès JWT

Dans la bibliothèque, cela se fait en définissant le paramètre de configuration "use_jwt_access_tokens" à ’true’ :

  1. $config = array(
  2.     ...
  3.     'use_jwt_access_tokens' => true,
  4.     ...
  5. );

Télécharger

Pour la configuration de OAuthSD, le fichier /commons/configure_oauth.php et le fichier configure_oidc.php définissent tous deux la constante USE_JWT_ACCESS_TOKENS, fixée à ’false’ par défaut.

Avec les flux OpenID Connect, lorsqu’il est configuré sur ’true’, le contrôleur Token émet à la fois le jeton d’accès et le jeton d’identification en tant que JWT.

  1.   access_token: string = "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJpZCI6ImYzNDExNjM3Mjc2ZjJjMzUxZjczYz
  2. ...
  3. VFx1Rk972S4ON1Dn6FwadTDS2U_A"
  4.   expires_in: long = 3600
  5.   token_type: string = "bearer"
  6.   scope: string = "openid profile"
  7.   id_token: string = "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJvMnNwLmRuYy5nbG9iYWwiLCJzdW...
  8. m6TFdfGhgjPyIA0w"

Télécharger

Use Case

Ecoutons Brent Shaffer [1] :

"vous pouvez utiliser les jetons d’accès JWT si vous avez des systèmes distribués et que vous devez valider l’authenticité d’un jeton auprès de plusieurs parties sans avoir à passer un appel réseau.

Par exemple, un jeton est attribué par le serveur d’autorisations. Ce jeton est un jeton Web JSON signé par la clé privée du serveur d’autorisations. Les serveurs de ressources (où les appels d’API sont effectués) sont répartis dans le monde entier et exécutent plusieurs applications. Tant que les serveurs de ressources disposent de la clé publique du serveur d’autorisations, qui n’a pas besoin d’être sécurisée, ils peuvent valider les jetons rapidement sans aucun appel réseau. Les jetons n’ont même pas besoin d’être persistés.

C’est un cas d’utilisation d’entreprise, mais il est très utile pour les systèmes distribués.."

Discussion

Cette fonctionnalité a été décrite en 2014, alors qu’OpenID Connect était en construction. Il visait probablement à donner aux utilisateurs d’OAuth2 la capacité de valider les jetons d’accès dans les serveurs de ressources.

Il faut également considérer le problème posé par OAuth : ce n’est pas un système d’authentification (mais d’autorisation) parce que le jeton d’accès est opaque et ne donne pas d’information sur l’utilisateur final. En incorporant l’ID de l’utilisateur dans le jeton JWT, on fait d’OAuth un système d’authentification ce qui permet de passer l’information sur l’utilisateur à des ressources protégées. C’est ce que Facebook fait avec ce qu’ils appellent le "signed request".

Avec cette fonctionnalité, les flux de codes d’autorisation OAuth 2.0 et OpenID Connect semblent très similaires.
Il faut cependant noter la différence entre jeton d’accès et jeton d’identité : ils ne sont notamment pas liés dans le temps. La notion de "session OIDC" repose sur la validité du jeton d’identité qui expire, alors que le jeton d’accès expire indépendamment, peut être rafraîchi ou peut ne pas expirer. Cette utilisation du jeton d’accès en tant que JWT ne peut donc remplacer le jeton d’identité d’OpenID Connect.

Les jetons JWT peuvent être utilisés en tant qu’OAuth 2.0 Bearer Tokens pour coder toutes les parties pertinentes d’un jeton d’accès dans le jeton d’accès lui-même au lieu d’avoir à les stocker dans une base de données. Cela pourrait s’avérer très utile avec les applications sans session (comme les applications à page unique), avec une latence réduite pour la validation de jeton.

Incorporer au jeton JWT des déclarations supplémentaires

  publié le par i-Tego WM

Les spécifications d’OAuth 2.0 autorisent l’ajout de nouvelles déclarations (claims) à la charge utile du jeton JWT.
Cet article montre comment cela est réalisé dans le cadre d’OAuthSD.

Cas d’usage

Il convient de s’assurer que l’utilisateur final est bien habilité à accéder à des ressources protégées, qu’il s’agisse de données personnelles ou que l’application soit propriétaire des données. On voudrait également pouvoir contrôler les données transmises par une ressource protégée à une application en fonction des droits de l’utilisateur final dans cette application.

Les administrateurs aussi bien que les utilisateurs finaux utiliseront OpenID Connect pour s’authentifier sur une application. On voudra donc moduler les droits et privilèges de l’utilisateur final en fonction de son identité, de son appartenance ou de son profil. L’exemple le plus simple est la distinction entre des droits de lecture des données et des droits de modification. On peut également envisager d’attribuer à l’utilisateur des droits d’administration de tout ou partie d’une application.

Ce n’est pas le rôle du serveur d’authentification, qui se doit d’être transparent par rapport aux portées d’autorisation (scopes) mis en oeuvre par une application. Pour cela, OAuthSD permet à une application externe d’incorporer des déclarations supplémentaires aux jetons JWT, qu’il s’agisse d’un jeton d’accès au format JWT ou d’un jeton d’identité. Les ressources protégées "sauront" interpréter les données du JWT pour contrôler leur réponse.

Remarque quant à la sécurité des données

Rappelons que la charge utile du jeton JWT n’est pas cryptée, mais seulement "URL64-encoded". Il convient donc de ne pas transmettre de cette façon des données sensibles.

L’intérêt de passer des données supplémentaires dans la charge utile du jeton JWT est de les lier de façon infalsifiable à l’identité du client et à celle de l’utilisateur à l’aide de la signature. Cela est particulièrement pertinent pour transmettre les privilèges d’un utilisateur ou d’une application à une ressource protégée. Dans cette optique, on peut :
- soit transmettre directement les privilèges ; par exemple un rôle qui prendrait des valeurs telles que ’administrateur’, ’redacteur’, ’propriétaire’, ’utilisateur’, etc., ou tout autre code compris par la ressource,
- soit transmettre les informations sur l’utilisateur qui permettront à la ressource protégée d’établir ses privilèges.

Mais attention dans ce deuxième cas : il ne faudra pas dévoiler de données sensibles sur l’utilisateur. Si la transmission de telles données à la ressource protégée s’avérait indispensable, la ressource protégée pourra interroger le service Userinfo.
On pourrait penser à un jeton crypté (JWE), mais cela nécessiterait, outre un développement spécifique, de s’écarter du standard OpenID Connect. Il serait beaucoup plus simple de crypter les informations supplémentaires en un seul bloc et de les passer dans une déclaration unique.

Il faut également faire attention à ne pas dépasser la longueur admise pour un JWT (à préciser).

Transmission de privilèges de l’utilisateur final avec l’authentification

OAuthSD offre deux méthodes complémentaires pour incorporer dans le JWT des informations relatives à un utilisateur final :

- l’écriture de données supplémentaires dans les champs ’profile’ et ’scope’ de la table users à l’aide du service HTTP Rest. Il s’agit d’un processus asynchrone dans la mesure où les données sont inscrites à un moment choisi par une application extérieure, indépendamment de l’interrogation du serveur d’autorisation.

OAuthSD utilise l’étendue d’autorisation (scope) "privileges" pour contrôler l’accès à ces informations. Quand une application présente le scope ’privileges’ dans la demande d’autorisation, les déclarations ’profil’ et ’scope’ sont inscrites dans la réponse Userinfo ainsi que dans la charge utile du jeton JWT. L’accès à l’API HTTP REST + TreeQL donne un bon exemple de transmission de privilèges.

- l’incorporation de données supplémentaires rafraîchies au moment de la fabrication du jeton JWT, par exemple en interrogeant un service externe. Il s’agit d’un processus synchrone ou de "scope dynamique", permettant d’obtenir des informations en phase avec celles détenues par les applications de gestion des utilisateurs dans le cas où cette gestion est extérieure au serveur.

Cette deuxième méthode est développée maintenant.

Incorporer des données supplémentaires à la charge utile du jeton JWT

Cette fonctionnalité est propre à une utilisation d’OAuthSD dans un groupe d’applications maîtrisées par une même organisation (Corporate Realm).

La bibliothèque oauth2-server-php offre une fonction reconfigurable pour insérer des déclarations supplémentaires dans un JWT au moment de sa création (depuis la version 1.10.0).

Pour incorporer des données supplémentaires, il faut :
- fixer la constante de configuration EXTRA_PAYLOAD à ’true’.
- définir le contenu de la fonction extra_payload() du fichier /oidc/interface/extrapayload.php.
La fonction extra_payload() doit être écrite par le concepteur des applications. Ce peut être un appel à un webservice tiers. La fonction reçoit les paramètres client_id, user_id et scope et doit retourner un array de ’claim’ => ’valeur’ qui sera combiné avec les déclarations de la charge utile sans pouvoir remplacer les déclarations standard.

Notes :
- Cette méthode permet de créer des déclarations ayant un nom quelconque. Cependant, les déclarations standard d’OIDC (’id’, ’jti’, ’iss’, ’aud’, ’sub’, ’exp’, ’iat’, ’token_type’ et ’scope’) ne pourront être surchargées : elles garderont leur nom et leur valeur. Si une déclaration ’scope’ est retournée, elle sera incorporée à la charge utile du JWT sous le nom "extra_scope".

- Les informations sont rafraîchies au moment de la création du jeton JWT ou de son rafraîchissement. Cela implique que ces informations ne sont pas strictement synchrones de leur correspondantes dans le système extérieur. Si les droits d’un utilisateur changent dans le système extérieur, il conviendra de révoquer la session de l’utilisateur afin de provoquer une nouvelle demande d’authentification ce qui entraînera la création d’un nouveau jeton JWT. Mais dans le cas où il serait nécessaire de modifier un grand nombre d’utilisateurs, une autre façon d’assurer le synchronisme sera de fermer le service le temps de la durée de vie d’un JWT.

- Cette méthode n’écrit pas dans la table users et, en particulier, dans le champ "scope". Si les déclarations supplémentaires comprennent une déclaration "scope" (ce qui sera généralement le cas), la valeur du champ "scope" de la table users sera incorporée à la charge utile du JWT sous le nom "user_scope".

- Dans tous les cas, la valeur du paramètre ’scope’ passé à l’appel d’authorize par l’application sera incorporé sous le nom ’requested_scope’ [1].

Voyez ci-dessous le code d’un exemple extrayant d’un web-service des données supplémentaires.

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

Notes

[1Le nom "scope" est malheureusement utilisé par Oauth 2.0 pour des concepts différents. Il est donc naturel d’utiliser des noms différents pour éviter de mélanger ou d’écraser des données.

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

  publié le par i-Tego WM

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’identité est la carte d’identité de l’utilisateur final et le passeport pour l’application cliente : il peut être utilisé à d’autres fins que la connexion de base.

Le jeton au format JWT présente les avantages suivants :
- C’est une spécification connue, largement adopté, et des bibliothèques client disponibles dans de nombreux langages (voyez Adaptation des applications).
- Il facilite la signature et la vérification à l’aide de bibliothèques cryptographiques validées.
- Puisqu’il peut être décodé au format JSON, cela permet d’inclure des métadonnées et des informations sur le jeton, ainsi que des données complémentaires pour l’application à laquelle il est adressé.

Tant que le serveur d’autorisations et le serveur de ressources s’accordent sur la signification du jeton, le contenu n’a aucune importance. Ce qui signifie que soit on colle au standard OpenID Connect, et alors le serveur de ressource est limité à UserInfo, soit on est dans un espace propriétaire (corporate realm) où l’on fait ce que l’on veut.

Avant d’aborder les utilisations possibles, il faut bien considérer que :
Le jeton d’identité doit être vérifié dès réception par l’application cliente ou la ressource protégée, avant toute utilisation.
Il existe deux méthodes : la validation locale du JWS ou l’introspection.

Ceci étant acquis, on peut citer les applications suivantes :

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 en mentionnant l’IP de l’appelant.
Il est possible de moduler la réponse de la ressource en fonction des privilèges de l’utilisateur en adaptant les scopes transmis à cet usage. Ceci est largement développé dans cet article : Définition des scopes OIDC et généralités sur leur utilisation par les applications et dans les articles connexes.

SaaS - Software as Service - Une des possibilités les plus intéressantes offertes par OAuthSD consiste à ajouter des déclarations dans le jeton d’identité. On peut ainsi coupler un système de vente en ligne à un serveur OIDC afin de louer des services en ligne. Le logiciel mis en location est configuré comme application cliente du serveur OIDC.
Au moment de l’authentification de l’utilisateur final, le module d’identification du contrôleur Authorize fait appel au système de vente en ligne pour vérifier que l’utilisateur est à jour de ses paiements.
Cette étape permet également de définir les privilèges de l’utilisateur final sur l’application, par exemple :
- la validation d’éventuelles options de l’application prises avec l’abonnement,
- des droits limités (consultation seule ...) au cours d’un délai de grâce après échéance,
- etc.
Les privilèges sont incorporés dans la charge utile du jeton d’identité (ID Token) sous la forme de déclarations supplémentaires. Les noms des déclarations et les valeurs transmises sont propres à l’application considérée.

DnC a développé le système DnC SaaS qui a très largement contribué à la mise au point du serveur OAuthSD.

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

Notes :
- ll s’agit bien de JWT stockés en cookie. Pas d’utiliser le JWT comme moyen d’assurer la rétention de session en passant le JWT dans l’URL, ce qui serait une aberration de sécurité.
- Le stockage des JWT par HTML5 Storage, ou toute autre méthode nécessitant JavaScript pour relire le jeton ouvre la porte à des attaques de type CSRF.

Voici un exemple qui montre comment utiliser le jeton pour simuler une session en chaînant les pages avec l’id de l’utilisateur final (user_id) :

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

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

Notes

[1Pourquoi se limiter à la page unique ? Remarquons que cette technique est particulièrement avantageuse pour gérer des applications multi-pages et/ou un ensemble d’applications à page unique.

Plus loin avec l’Introspection

  publié le par i-Tego WM

En l’absence d’une norme stable, la bibliothèque oauth2-server-php de Brent Shaffer n’a pas de point d’extrémité Introspection.

OAuthSD, comme de nombreux serveurs d’autorisation, implémente son propre contrôleur pour l’Introspection. Il a été développé "au-dessus" de la bibliothèque.

Que pourrait être une "implémentation minimale et compatible" du contrôleur Introspection pour la bibliothèque ? Avant de commencer à coder, examinons le projet de RFC 7662 OAuth 2.0 Token Introspection.

À propos de la nécessité pour le Serveur de Ressource de s’authentifier

Selon rfc7662 Section 2.1. :

"L’appelant DOIT avoir une autorisation pour accéder à ce point de terminaison".

(Ce que nous aurions pu faire pour se conformer à cette spécification :
Cette implémentation attend un jeton d’accès émis pour un client enregistré (non public).
La provenance de ce jeton d’accès dépend du service.
Il a peut-être été obtenu du serveur d’autorisation par l’appelant en tant qu’application cliente.
Ou bien, si l’appelant est un serveur de ressources interrogé par une application cliente, le jeton lui a peut-être été transmise par l’appelant.)

Nous soumettons à la sagacité du lecteur les observations suivantes :

- Le but de cette autorisation est "Pour empêcher les attaques par balayage de jetons".
Ce type d’attaques est généralement géré au niveau du pare-feu en relation avec le service.
Par exemple, avec Apache, nous pourrions définir une authentification HTTP Basic au niveau du répertoire.
Ensuite, sur un serveur géré WHM / cPanel, nous pourrions utiliser CSF / LFD pour bloquer les échecs de connexion répétitifs.
Nous pourrions également configurer à cette fin une règle Apache Modsec particulière.
Plus généralement, l’attenuation des attaques est mieux effectuée en amont plutôt qu’au niveau de l’application. Cela induit une charge de calcul moindre, voire nulle, sur le serveur d’autorisations.

- Pour que le serveur de ressources s’authentifie, il doit être enregistré en tant qu’application cliente [1], ce qui suppose qu’il appartient au domaine de l’entreprise.
Ceci est vrai dans la plupart des cas, mais nous pouvons désirer que tout serveur de ressources étranger puisse vérifier un JWT.

À propos du but de l’Introspection

Valider la signature
La charge utile du jeton JWT n’est pas cryptée, seulement encodée. Son contenu est immédiatement lisible par le RS avant la validation de la signature.
Donc, le RS a besoin d’une introspection pour vérifier que la signature est valide (si elle ne la valide pas localement en utilisant la clé publique).

Authentifier l’application qui appelle le serveur de ressource (RS)
De plus, le but de transmettre un jeton à un RS est de lui indiquer s’il peut répondre à l’application requérante et quelles données il peut lui transmettre dans sa réponse.
Un attaquant pourrait avoir volé un JWT et l’utiliser avec sa propre application. Le risque est que le RS réponde à une application et / ou à un utilisateur non autorisé. Il est donc essentiel d’authentifier l’application qui interroge le RS pour l’empêcher de répondre à un malware (voir plus loin).

Cela dit, quelle tâche spécifique doit être accomplie par l’Introspection et quelle doit être la réponse ?

À propos de la réponse de l’Introspection

Selon la section 2.2. de rfc7662, le seul membre requis est "actif", un indicateur booléen indiquant la validité du jeton vu depuis le serveur qui l’a émis.
Cela va au-delà de la validation de la signature car cela "indique qu’un jeton donné a été émis par ce serveur d’autorisation, n’a pas été révoqué par le propriétaire de la ressource, ...".

Mais inclure dans cette valeur la condition "et se trouve dans sa fenêtre temporelle de validité donnée" ne parait pas pertinent : le RS possède déjà les informations de la charge utile. En revanche, le jeton pourrait avoir été invalidé au niveau du serveur, et c’est cette information que l’introspection apportera. Au passage, notons qu’il n’y a pas de lien entre la durée de vie du jeton d’accès et celle du jeton d’identité. Il y a une possibilité de révoquer le jeton d’accès, mais qu’en est-il du jeton d’identité ?

Notez que l’Introspection devrait également être utilisable pour valider un jeton d’accès JWT, avec des membres moins nombreux et différents. C’est une autre raison pour ne pas spécifier les membres de façon autoritaire.

Empêcher le Serveur de Ressource de répondre à un malware

C’est une question centrale !

La spécification reconnaît la question, mais ne donne pas une réponse normalisée. Il est indiqué dans la section 2.1 :

"Le point de terminaison d’introspection PEUT accepter d’autres paramètres OPTIONNELS pour fournir un contexte supplémentaire à la requête. Par exemple, un serveur d’autorisation peut souhaiter connaître l’adresse IP du client accédant à la ressource protégée pour déterminer si le client présentant le jeton est le bon. La définition de ce paramètre ou de tout autre paramètre sort du cadre de la présente spécification, ... "

Nous proposons mettre ceci en œuvre de la manière suivante :
Si le RS appelant le point de terminaison Introspection transmet l’adresse IP de son propre demandeur dans le ’requester_ip’, nous devons vérifier que cette adresse IP se trouve dans le même sous-réseau que l’application cliente identifiée par la déclaration ’aud’. Voyez : Vérification de l’origine de la requête reçue par un serveur de ressource.

À propos de l’adaptation de la réponse au Serveur de Ressource

Il est indiqué à la section 2.2 :

"Le serveur d’autorisation PEUT répondre différemment à différentes ressources protégées qui font la même demande. Par exemple, un serveur d’autorisation PEUT limiter les étendues d’un jeton donné qui sont renvoyées à chaque ressource protégée afin d’empêcher une ressource protégée d’en apprendre davantage sur l’ensemble plus vaste d’applications du réseau que nécessaire pour son fonctionnement. "

Cela semble très difficile à mettre en œuvre. Dans l’exemple donné, qui traite des portées, le RS doit faire appel à l’introspection avec plus de paramètres ou le AS doit avoir des informations sur le RS. En somme, il s’agit de prendre en compte les scopes au niveau de l’introspection pour définir les autorisations.

Cette prescription peut être appliquée dans un domaine d’entreprise pour des applications particulières, elle ne sera donc pas prise en compte dans notre "implémentation minimale et compatible".

Nous pensons que les portées définissant les droits ont une signification qui devrait être interprétée par la ressource protégée elle-même, le serveur d’authentification devant les transmettre de manière transparente. Voyez : Incorporer au jeton JWT des déclarations supplémentaires.

... ou ne répond pas du tout

Il est indiqué dans la section 4 :

"Si le jeton ne peut être utilisé que sur certains serveurs de ressources, le serveur d’autorisation DOIT déterminer s’il peut ou non être utilisé sur le serveur de ressources effectuant l’appel d’introspection".

Cette prescription n’est rien de plus qu’une déclinaison radicale de la précédente. Nous allons la traiter de la même manière.

Proposition d’un contrôleur d’introspection pour la bibliothèque

Une "implémentation minimale et compatible" du contrôleur Introspection aurait les caractéristiques suivantes :
- se limitera à la validation de la signature JWT avec l’option de vérification de l’adresse IP du client (paramètre ’requester_ip’), sans aucune hypothèse quant à l’utilisation du jeton,
- ne nécessitera pas l’authentification de l’appelant,
- acceptera le jeton porteur de préférence pour poster un jeton,
- peut répondre comme indiqué dans le RFC.

 Suivez ce développement sur GitHub
Voyez le fork :
https://github.com/bdegoy/oauth2-server-php
Testez avec :
https://github.com/bdegoy/oauth2-server-php-introspection-test
Discutez-en :
https://github.com/bshaffer/oauth2-server-php/pull/964

Notes

[1Ceci est l’option prise par IBM

JWE (JSON Web Encryption) Jeton JWT avec contenu crypté

  publié le par i-Tego WM

La spécification JWE (JSON Web Encryption) standardise la façon de représenter un contenu chiffré dans une structure de données basée sur JSON. C’est une spécification générale, qui s’applique au cryptage de la charge utile du JWT.

OAuthSD permet aux applications clientes d’échanger des données protégées avec des jetons JWE authentifiés.

Comme avec JWS, il n’est pas question d’appliquer dans sa totalité le "standard" JOSE dont les multiples options et la complexité ouvrent de grandes failles de sécurité.

Référence : RFC 7516

Ce document définit deux formes de sérialisation pour représenter la charge utile chiffrée : la sérialisation compacte JWE et la sérialisation JWE JSON.
Nous traiterons la sérialisation compacte, qui est la seule à produire un JWT sécurisé pour l’URL (Url safe).

Sérialisation compacte JWE

Avec la sérialisation compacte JWE, un jeton JWT est construit avec cinq composants clés, chacun séparé par un point (.) : En-tête JOSE, clé cryptée JWE, vecteur d’initialisation JWE, données d’authentification supplémentaires (AAD) JWE, charge utile chiffrée JWE et balise d’authentification JWE :

JOSE header . JWE Encrypted Key . JWE initialization vector  . JWE Ciphertext . JWE Authentication Tag

Cryptage symétrique, Mode de gestion de clé

Le principe général du JWE consiste à crypter un contenu avec un algorithme de cryptage symétrique. Il est donc nécessaire que les deux parties, émetteur et destinataire, utilisent une même clé de chiffrement du contenu (CEK) .

Le Mode de gestion de clé est la façon d’indiquer au destinataire comment il doit déterminer la valeur de la clé CEK.

Traduction d’un extrait de la RFC 7516 :

Mode de gestion de clé
Méthode de détermination du chiffrement de la valeur de clé de chiffrement du contenu (CEK) à utiliser.
Chaque algorithme utilisé pour déterminer la valeur CEK utilise un Mode de gestion de clé. Les Modes de gestion de clé utilisés par cette spécification sont : (...)

Chiffrement de clé (Key Encryption)
Un mode de gestion des clés dans lequel la valeur CEK est chiffrée pour le
destinataire prévu en utilisant un algorithme de chiffrement asymétrique.

Encapsulation de clé (Key Wrapping)
Un mode de gestion des clés dans lequel la valeur CEK est chiffrée pour le
destinataire prévu à l’aide d’un algorithme d’encapsulation de clé symétrique.

Accord de clé directe (Direct Key Agreement)
Un mode de gestion des clés dans lequel un algorithme d’accord de clé est utilisé
pour convenir de la valeur CEK.

Accord de clé avec encapsulation de clé (Key agreement with Key Wrapping)
Un mode de gestion des clés dans lequel un algorithme d’accord de clé est utilisé
pour convenir d’une clé symétrique utilisée pour chiffrer la valeur CEK pour le
destinataire prévu à l’aide d’un algorithme d’encapsulation de clé symétrique.

Cryptage direct (Direct Encryption)
Un mode de gestion de clé dans lequel la valeur CEK utilisée est la valeur du secret de clé symétrique partagé entre les parties.

Deux modes sont particulièrement utilisés : le cryptage direct ( symétrique ) pour sa simplicité et le chiffrement de clé ( asymétrique ) qui est parfaitement adapté à des applications inscrites sur un serveur OIDC.

Mode de gestion "Cryptage direct" : Choix de la clé de chiffrement

Dans ce mode, le contenu du JWE est crypté avec un algorithme symétrique. Pour le décrypter, nous avons besoin d’une clé CEK connue à la fois par l’application émettant le JWE et le serveur OAuthSD. Le mode de gestion de clé "Cryptage direct" est le mode de partage le plus simple : la clé est un secret partagé "hors ligne" entre les deux parties.

La clé CEK ne doit pas être la clé publique d’un client, ni aucune autre donnée publique !
Il ne peut pas non plus s’agir de la clé privée d’un client, car elle doit rester secrète à l’intérieur de l’OP.

Cas d’un "jeton au porteur" (Bearer Token)
Lorsqu’une ressource protégée présente à l’Introspection un jeton en provenance d’une application, il s’agit d’un "jeton au porteur" (Bearer Token).

Lorsque le contrôleur d’Introspection reçoit le JWE, il ne peut pas déterminer l’application avant le décryptage (contrairement au cas d’un JWT). Ainsi, le secret CEK ne peut être qu’une valeur prédéterminée à l’intérieur d’un groupe privé d’applications et de ressources protégées, contrôlées par un même serveur OAuthSD.

Cas d’un jeton JWE utilisé dans un protocole PoP
Dans le cas de la preuve de possession apportée par une application (PoP), ou d’autres protocoles d’identification d’application mis en œuvre par OAuthSD, l’application qui présente le jeton JWE communique son identifiant client_id en même temps que le jeton avec le paramètre supplémentaire ’aud’ (audience).

Il est alors possible d’utiliser le secret de l’application cliente comme clé de chiffrement de contenu (CEK).

Il faudra veiller à ce que le secret ne soit pas révélé par une mauvaise utilisation dans le cadre d’une application sans backend.

Avantages et inconvénients du cryptage direct

Le cryptage direct est le plus simple à programmer, et donc le plus souvent utilisé. Mais il est compliqué à exploiter, car le partage "hors ligne" de la clé avec de nombreux clients est fastidieux, et la mise à jour plus encore. A cela s’ajoute le risque de compromission inhérent à une transmission "hors ligne".

Mode de gestion "Chiffrement de clé"

Dans le cas de jetons JWE émis par des applications clientes enregistrées sur le serveur OAuthSD, nous avons tout ce qu’il faut pour gérer un chiffrement asymétrique de la clé CEK, une paire de clés publique/privée étant attachée à chaque application.

La clé publique sera utilisée pour le chiffrement.

Pour le déchiffrement, il n’est pas question de passer la clé privée à une application publique. Il ne serait pas non plus de bonne pratique de la passer à une application de confiance, sauf peut-être sur le même serveur ou groupe de serveurs co-localisés. Le déchiffrement et la validation du jeton sera donc effectué sur le serveur, avec un contrôleur Introspect adapté à JWE.

Introspection d’un jeton JWE

Dans le serveur OAuthSD , le contrôleur Introspection peut recevoir un jeton d’identité ID chiffré avec JWE aussi bien qu’un jeton ID JWT standard.
Si un jeton JWE est reconnu, il est déchiffré et le processus se poursuit avec la charge utile du JWE, qui n’est autre que le JWT.

Cas d’usage de JWE

Dans l’utilisation courante du jeton d’identité (ID Token), la charge utile ne comporte pas de données sensibles.
Cependant, dans le cas où des données supplémentaires sont incorporées dans le jeton, comme par exemple :
- des droits particuliers accordés à un utilisateur,
- des éléments de configuration de l’application cible en fonction d’un utilisateur, etc.
Il devient utile de masquer ces données.

Rien n’empêche également de se servir du JWE de façon extensive pour transporter des informations de façon protégée, ce qui, dans certaines applications, permettrait d’éviter d’exposer des données sensibles à l’aide de web services (ou ressources protégées).

Attention ! JOSE n’est pas sûr !

https://paragonie.com/blog/2017/03/jwt-json-web-tokens-is-bad-standard-that-everyone-should-avoid

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