IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)

Vous êtes nouveau sur Developpez.com ? Créez votre compte ou connectez-vous afin de pouvoir participer !

Vous devez avoir un compte Developpez.com et être connecté pour pouvoir participer aux discussions.

Vous n'avez pas encore de compte Developpez.com ? Créez-en un en quelques instants, c'est entièrement gratuit !

Si vous disposez déjà d'un compte et qu'il est bien activé, connectez-vous à l'aide du formulaire ci-dessous.

Identifiez-vous
Identifiant
Mot de passe
Mot de passe oublié ?
Créer un compte

L'inscription est gratuite et ne vous prendra que quelques instants !

Je m'inscris !

Python : générer un JSON Web Token pour ensuite s'authentifier sur un serveur web,
Un billet blog de Denis Hulo

Le , par User

0PARTAGES

I. Introduction

JSON Web Token (JWT) est un standard ouvert défini dans le document RFC 75191. Il permet l'échange sécurisé de jetons (tokens) entre plusieurs parties.

Cette sécurité de l’échange se traduit par la vérification de l'intégrité et de l'authenticité des données. Elle s’effectue par l'algorithme HMAC ou RSA.

On souhaite dans ce billet montrer comment créer un JWT en Python, pour nous permettre ensuite de nous authentifier sur un serveur web et pouvoir ainsi y récupérer des données.

II. Structure d'un JSON Web Token

Un JSON Web Token ou JWT est composé de trois parties :

  • Un en-tête (header), utilisé pour décrire le jeton. Il s'agit d'un objet JSON.
  • Une charge utile (payload) qui représente les informations embarquées dans le jeton. Il s'agit également d'un objet JSON.
  • Une signature numérique.




II-A. En-tête

Il comporte au moins 2 paramètres :

  • typ: spécifie le type de jeton. On passe toujours la valeur "JWT" à ce paramètre.
  • alg: algorithme utilisé pour signer le jeton. On choisira dans notre cas la valeur "HS256" pour ce paramètre, et donc on utilisera l'algorithme HMAC SHA-256 pour signer le jeton.


Ce qui donne pour le header :

Code JavaScript : Sélectionner tout
1
2
3
4
{ 
  "typ": "JWT", 
  "alg": "HS256" 
}


II-B. Charge utile

La partie charge utile ou payload a dans notre cas comme paramètres :

  • name: nom de l'émetteur du jeton.
  • iat : date de création du jeton. On prend l'heure Unix, c'est à dire le nombre de secondes écoulées depuis le 1ᵉʳ janvier 1970 00:00:00 UTC.


On peut ainsi avoir pour le payload :

Code JavaScript : Sélectionner tout
1
2
3
4
{ 
   "name": "John Bradley", 
   "iat": 1688462212 
}

Si vous souhaitez avoir la liste des paramètres standards utilisés pour la charge utile, je vous invite à consulter la section Champs standards (claims) de la page Wikipedia JSON Web Token.

II-C. Signature

1. Pour obtenir la signature, il faut tout d'abord encoder séparément l'en-tête et la charge utile avec base64url, puis les concaténer en les séparant par un point :

base64UrlEncode(header) + "." + base64UrlEncode(payload)

2. On utilise ensuite l'algorithme HS256 défini dans l'en-tête pour obtenir la signature de ce résultat. La clé utilisée par l'algorithme correspond à la clé privée :

HMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload), clé_privée )

Cette signature est ajoutée de la même manière au résultat obtenu en 1. (encodée et séparée par un point).

La chaîne complète obtenue représente le JWT recherché.

Vous pouvez d'ailleurs créer un JWT en ligne sur le site https://jwt.io/.

On obtient ainsi en reprenant notre exemple et en choisissant "ma super clé secrète" comme clé privée :



Note : on veillera à conserver la clé secrète de façon extrêmement sécurisée comme il est indiqué dans la section Vulnérabilités de la page Wikipedia JSON Web Token.

III. Implémentation en Python

III-A. Encodage et décodage d'un JSON Web Token

La librairie PyJWT permet d'encoder et de décoder un JSON Web Token.

Installation de la librairie :

pip install pyjwt

La fonction encode de la librairie PyJWT effectue l'encodage d'un JWT à partir de la charge utile, de la clé secrète et de l'algorithme choisi.

Exemple d'utilisation :

Code Python : Sélectionner tout
1
2
3
4
5
6
7
import jwt 
>>> payload = {"name": "John Bradley", "iat": 1688462212} 
>>> encoded_jwt = jwt.encode(payload, "ma super clé secrète", algorithm="HS256") 
>>> print(encoded_jwt) 
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJuYW1lIjoiSm9obiBCcmFkbGV5IiwiaWF0IjoxNjg4NDYyMjEyfQ.a8LZrE5l3G2BnQbA6241QSet_5JWdZJ3VKYPnrqASn4 
>>> jwt.decode(encoded_jwt, 'ma super clé secrète', algorithms="HS256") 
{'name': 'John Bradley', 'iat': 1688462212}

Note : il peut arriver que la clé soit encodée en Base64, il faut alors la décoder avant de la transmettre à la fonction encode.

III-B. Récupération de données provenant d'un serveur web

La librairie Requests permet d'envoyer très simplement des requêtes HTTP à un serveur web.

Installation de la librairie :

pip install requests

Exemple d'utilisation :

Code Python : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
>>> import requests 
>>> r = requests.get('https://api.github.com/user', auth=('user', 'pass')) 
>>> r.status_code 
200 
>>> r.encoding 
'utf-8' 
>>> r.text 
'{"type":"User"...' 
>>> r.json() 
{'private_gists': 419, 'total_private_repos': 77, ...}

On doit dans notre cas récupérer des données provenant d'un serveur web à partir d'une clé de souscription et d'un JWT obtenu à l'aide de la fonction encode.

Le header de la requête HTTP devrait ressembler à cela :

Ocp-Apim-Subscription-Key: <clé de souscription>
Authorization: Bearer <JWT Token>


On donne maintenant le code complet de la fonction :

Code Python : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
def get_data_product(api_key, api_token, product_id): 
    # récupération de données provenant d'un serveur web à partir de la clé secrète, du jeton et de l'identifiant du produit recherché 
    try: 
        # composition de l'en-tête de la requête avec la clé de souscription et le JWT 
       headers = {'Ocp-Apim-Subscription-Key': api_key, 'Authorization': 'Bearer {token}'.format(token=api_token)} 
  
        # exécution d'une requête get sur le serveur web pour récupérer les données sur le produit 
        r = requests.get('https://webplatform.com/read/api/products/{product_id}'.format(product_id=product_id), headers=headers) 
  
        # on renvoie les données sur le produit au format json 
        return r.json() 
  
    # gestion de l'erreur 
    except requests.exceptions.RequestException as e 
        print(e)

L'en-tête de la requête est donc bien composé de la clé de souscription Apim et du JWT :

Code Python : Sélectionner tout
headers = {'Ocp-Apim-Subscription-Key': api_key, 'Authorization': 'Bearer {token}'.format(token=api_token)}

Module complet de test :

Code Python : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
import time # module contenant la fonction time() qui renvoie l'heure Unix 
import jwt # librairie utilisée pour encoder le JWT à partir de la charge utile, de la clé secrète et de l'algorithme choisi 
import requests # librairie permettant d'exécuter des requêtes HTTP 
  
def get_data_product(api_key, api_token, product_id): 
    # récupération de données provenant d'un serveur web à partir de la clé secrète, du jeton et de l'identifiant du produit recherché 
    try: 
        # composition de l'en-tête de la requête avec la clé de souscription et le JWT 
        headers = {'Ocp-Apim-Subscription-Key': api_key, 'Authorization': 'Bearer {token}'.format(token=api_token)} 
  
        # exécution d'une requête get sur le serveur web pour récupérer les données sur le produit 
        r = requests.get('https://webplatform.com/read/api/products/{product_id}'.format(product_id=product_id), headers=headers) 
  
        # on renvoie les données sur le produit au format json 
        return r.json() 
  
    # gestion de l'erreur 
    except requests.exceptions.RequestException as e: 
        print(e) 
  
  
# heure Unix de création du jeton : nombre de secondes écoulées depuis le 1ᵉʳ janvier 1970 00:00:00 UTC. 
iat = int(time.time()) 
  
# charge utile 
payload = {"name": 'John Bradley', "iat": iat} 
  
# clé secrète           
api_key = "ma super clé secrète" 
  
# affiche l'en-tête, la charge utile et la clé secrète 
print("Header") 
print({"typ": "JWT", "alg": "HS256"});print() 
  
print("Payload") 
print(payload); print() 
  
print("Clé secrète") 
print(api_key); print() 
  
# encodage du JWT à partir de la charge utile, de la clé et de l'algorithme 
api_token = jwt.encode( payload=payload, key=api_key, algorithm='HS256') 
  
# affiche le résultat de l'encodage du jeton 
print("Résultat de l'encodage du JWT") 
print(api_token); print() 
  
# récupération des données à partir de la clé de souscription (api_key), du JWT (api_token), et de l'identifiant du produit 
data_product = get_data_product(api_key, api_token, 'p173795') 
  
# affiche les informations sur le produit  
print(data_product)

Le code affiche pour la partie génération du JWT :



Note : les paramètres du JWT et de l'en-tête de la requête doivent bien sûr être adaptés à chaque cas.

IV. Conclusion

Après avoir décrit la structure d'un JWT et les différentes étapes nécessaires pour le générer, nous avons pu proposer une implémentation en Python avec une fonction permettant de récupérer des données provenant d'un serveur web.

Chacun pourra ensuite librement adapter les paramètres du JWT et de la requête à ses besoins.

Sources :

https://fr.wikipedia.org/wiki/JSON_Web_Token
https://datatracker.ietf.org/doc/html/rfc7519
https://jwt.io/
https://fr.wikipedia.org/wiki/Base64
https://pyjwt.readthedocs.io/en/latest/
https://requests.readthedocs.io/en/latest/

Une erreur dans cette actualité ? Signalez-nous-la !