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

User

[Actualité] Python : générer un JSON Web Token pour ensuite s'authentifier sur un serveur web

Noter ce billet
par , 09/07/2023 à 21h40 (5430 Affichages)
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.


Nom : JWT.png
Affichages : 4479
Taille : 27,0 Ko


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 - Visualiser dans une fenêtre à part
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 - Visualiser dans une fenêtre à part
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 :

Nom : jwt.io.png
Affichages : 3230
Taille : 59,0 Ko


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 - Visualiser dans une fenêtre à part
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 - Visualiser dans une fenêtre à part
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 - Visualiser dans une fenêtre à part
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 - Visualiser dans une fenêtre à part
headers = {'Ocp-Apim-Subscription-Key': api_key, 'Authorization': 'Bearer {token}'.format(token=api_token)}

Module complet de test :

Code Python : Sélectionner tout - Visualiser dans une fenêtre à part
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 :

Nom : resultat.png
Affichages : 3180
Taille : 13,4 Ko


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/

Envoyer le billet « Python : générer un JSON Web Token pour ensuite s'authentifier sur un serveur web » dans le blog Viadeo Envoyer le billet « Python : générer un JSON Web Token pour ensuite s'authentifier sur un serveur web » dans le blog Twitter Envoyer le billet « Python : générer un JSON Web Token pour ensuite s'authentifier sur un serveur web » dans le blog Google Envoyer le billet « Python : générer un JSON Web Token pour ensuite s'authentifier sur un serveur web » dans le blog Facebook Envoyer le billet « Python : générer un JSON Web Token pour ensuite s'authentifier sur un serveur web » dans le blog Digg Envoyer le billet « Python : générer un JSON Web Token pour ensuite s'authentifier sur un serveur web » dans le blog Delicious Envoyer le billet « Python : générer un JSON Web Token pour ensuite s'authentifier sur un serveur web » dans le blog MySpace Envoyer le billet « Python : générer un JSON Web Token pour ensuite s'authentifier sur un serveur web » dans le blog Yahoo

Mis à jour 28/08/2023 à 07h36 par User

Catégories
Programmation , Python , Algorithmique

Commentaires