Afficher/cacher Sommaire
JSON Web Token (JWT)
JWT ou JSON Web Token est un standard ouvert décrit dans la RFC 7519 qui permet l’authentification d’un utilisateur à l’aide d’un jeton (token) signé
Voir article Sécuriser une API REST (Partie 1) : Théorie qui explique le JWT.
- Lors du premier échange, le client envoie son couple login/mot de passe au serveur;
- Si le couple est valide, le serveur génère un token et l’envoie au client. Ce token permettra d’authentifier l’utilisateur lors des prochains échanges;
- Le client stocke ensuite le token en local;
- Le token est renvoyé, par le client, pour chaque appel à l’API (via l’en-tête HTTP « Authorization ») permettant ainsi d’authentifier l’utilisateur.
Les Tokens Web JSON se composent généralement de trois parties: un en-tête header, une charge utile payload et une signature.
Header
Objet JSON encodé en base64 qui représente l’en-tête du token avec 2 éléments
- Le type du token;
- L’algorithme utilisé pour la signature.
{
"typ":"JWT",
"alg":"HS256"
}
HS256 indique que ce jeton token est signé avec HMAC-SHA256.
Payload
Objet JSON encodé en base64 qui représente le corps du token pour y mettre toute information utile au serveur. Le standard définit trois types de propriétés (appelées claims) :
- réservées : Il s’agit de noms réservés définis par la spécification. On y retrouve notamment :
- “iss” (Issuer) : Identifie le serveur ou le système qui a émis le token;
- “sub” (Subject) : Identifiant de l’utilisateur que le token représente;
- “aud” (Audience) : Application ou site qui reçoit le token;
- “iat” (Issued At) : Date de génération du token;
- “exp” (Expiration Time) : Date d’expiration du token.
- publiques : Noms normalisés tels que “email”, “name”, “locale”, etc… (JSON Web Token Claims , voir tableau ci-joint)
Claim Name | Claim Description |
---|---|
iss | Issuer |
sub | Subject |
aud | Audience |
exp | Expiration Time |
nbf | Not Before |
iat | Issued At |
jti | JWT ID |
name | Full name |
given_name | Given name(s) or first name(s) |
family_name | Surname(s) or last name(s) |
middle_name | Middle name(s) |
nickname | Casual name |
preferred_username | Shorthand name by which the End-User wishes to be referred to |
profile | Profile page URL |
picture | Profile picture URL |
website | Web page or blog URL |
Preferred e-mail address | |
email_verified | True if the e-mail address has been verified |
gender | Gender |
birthdate | Birthday |
zoneinfo | Time zone |
locale | Locale |
phone_number | Preferred telephone number |
phone_number_verified | True if the phone number has been verified |
address | Preferred postal address |
updated_at | Time the information was last updated |
azp | Authorized party - the party to which the ID Token was issued |
nonce | Value used to associate a Client session with an ID Token |
auth_time | Time when the authentication occurred |
at_hash | Access Token hash value |
c_hash | Code hash value |
acr | Authentication Context Class Reference |
amr | Authentication Methods References |
sub_jwk | Public key used to check the signature of an ID Token |
cnf | Confirmation |
sip_from_tag | SIP From tag header field parameter value |
sip_date | SIP Date header field value |
sip_callid | SIP Call-Id header field value |
sip_cseq_num | SIP CSeq numeric header field parameter value |
sip_via_branch | SIP Via branch header field parameter value |
orig | Originating Identity String |
dest | Destination Identity String |
mky | Media Key Fingerprint String |
- privées : Propriétés à définir pour répondre aux besoins de votre application.
/!\le payload ne doit pas contenir de données sensibles
Signature
Hash des deux premières parties du token réalisé en utilisant l’algorithme qui est précisé dans le header,l’algorithme utilisé ci-dessous est HS256 (HMAC-SHA-256).
HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload), 'secret')
L’algorithme utilise une clé secrète (détenue par le serveur), utilisée pour signer les tokens mais également s’assurer de la validité de ceux-ci en vérifiant leur signature.
Expiration d’un token
JWT possède une date d’expiration (propriété “exp” du payload).
La durée de validité d’un JWT va dépendre du type de données échangées (quelques minutes pour des données sensibles à plusieurs heures pour les non sensibles).
Pour éviter à l’utilisateur une réauthentification lorsque le JWT expire,utiliser un “refresh token”.
Lorsque l’utilisateur s’identifie à l’aide de son couple login/mot de passe, le serveur génère en plus du JWT, un token aléatoire à usage unique attaché à l’utilisateur (généralement sauvegardé en base de données).
Utilisation refresh token
Lorsque le JWT est expiré :
- Le client (application ou site) envoie une requête avec le JWT expiré et le “refresh token”
- Le serveur vérifie que le JWT est expiré, que le “refresh token” est valide et que celui-ci est bien associé à l’utilisateur du JWT expiré
- Le serveur génère un nouveau JWT et “refresh token” (lié à l’utilisateur) et invalide l’ancien “refresh token” (par exemple suppression de la base de données ou un champ indiquant que ce token n’est plus valide)
- Le serveur renvoie le nouveau JWT et “refresh token” au client;
- Le client sauvegarde le JWT et le “refresh token”;
- Le client peut utiliser le nouveau JWT.
Possibilité d’ajouter une date d’expiration au refresh token .
Créer un jeton Web JSON (Json Web Token) en PHP
La section suivante décrit étape par étape comment créer un jeton Web JSON en PHP. Pour créer un jeton Web JSON, nous utilisons les deux méthodes PHP décrites ci-dessous:
string json_encode (mixed $value [, int $options = 0 [, int $depth = 512 ]] )
— Renvoie la représentation JSON d’une valeur où l’argument $value peut être n’importe quel type sauf une ressourcestring base64_encode ( string $data )
— code les données données en base64.
1-Encodage base64 “header JSON Object”
La première étape dans la création d’un JWT est de définir la première partie du jeton token, qui est l’en-tête header, puis l’encoder en base64, en utilisant les deux méthodes PHP
Création ,droits et édition du fichier jwt-header.php
touch jwt-header.php && chmod +x jwt-header.php && nano jwt-header.php
#!/usr/bin/php
<?php
//setting the header: 'alg' => 'HS256' indicates that this token is signed using HMAC-SHA256
$header = [
'typ' => 'JWT',
'alg' => 'HS256'
];
// Returns the JSON representation of the header
$header = json_encode($header);
//encodes the $header with base64.
$header = base64_encode($header);
//list the resulted header
print_r($header);
?>
Exécution et sortie
./jwt-header.php
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9
2-Encodage base64 “payload JSON Object”
Après avoir créé l’en-tête, nous devons créer lepayload:
$payload = ["country" => "France", "name" => "Ansel-Arsenault", "email" => AnselArsenault@jourrapide.com ];
Pour coder le payload, nous utilisons à nouveau les deux méthodes json_encode et base64_encode
$payload = json_encode($payload);
$payload = base64_encode($payload);
3-Concaténer header et payload avec le point “.” comme séparateur.
Après avoir encodé le payload, nous devons concaténer header avec payload comme ceci:
$header.$payload
Créer le php
touch jwt-header-payload.php && chmod +x jwt-header-payload.php && nano jwt-header-payload.php
Le listing suivant affiche l’en-tête et la charge utile (header et payload)
#!/usr/bin/php
<?php
$header = [
'typ' => 'JWT',
'alg' => 'HS256'
];
$header = json_encode($header);
$header = base64_encode($header);
//setting the payload
$payload = [
"country" => "France",
"name" => "Ansel-Arsenault",
"email" => "AnselArsenault@jourrapide.com "
];
// Returns the JSON representation of the payload
$payload = json_encode($header);
//encodes the $payload with base64.
$payload = base64_encode($header);
echo "$header.$payload";
?>
Exécution et sortie
./jwt-header-payload.php
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.ZXlKMGVYQWlPaUpLVjFRaUxDSmhiR2NpT2lKSVV6STFOaUo5
4-Calculer la “signature” de l’en-tête “header” et de la charge utile “payload”
La prochaine étape dans la création d’un JWT en PHP consiste à définir la signature en la générant en utilisant la méthode HMAC.
$signature = hash_hmac('sha256','$header.$payload', $key, true);
Vous remarquerez que l’argument $key est utilisé pour générer la signature. Il représente votre mot de passe personnel et est utilisé pour valider la signature, comme vous le verrez dans cet article. Dans ce cas, la clé $key is ‘Ansel-Signe-Arsenault’.
touch jwt-signature.php && chmod +x jwt-signature.php && nano jwt-signature.php
#!/usr/bin/php
<?php
//setting the personal key identification
$key = 'Ansel-Signe-Arsenault';
$header = [
'typ' => 'JWT',
'alg' => 'HS256'
];
$header = json_encode($header);
$header = base64_encode($header);
$payload = [
"country" => "France",
"name" => "Ansel-Arsenault",
"email" => "AnselArsenault@jourrapide.com "
];
$payload = json_encode($header);
$payload = base64_encode($header);
// Generates a keyed hash value using the HMAC method
$signature = hash_hmac('sha256','$header.$payload', $key, true);
echo $signature;
?>
Exécution et sortie
./jwt-signature.php
���[�2�1�� ���sÿ>Z�a�@��)
5-Concaténer “signature”,”header” et “payload”.
Avant d’effectuer la dernière étape dans la création du jeton token final, nous avons besoin de coder également la signature base64, en utilisant cette ligne de code:
$signature = base64_encode($signature);
La dernière étape dans la création de notre jeton token est de concaténer toutes les chaînes: l’en-tête header, la charge utile payload et la signature en utilisant cette ligne:
$token = "$header.$payload.$signature";
Le fichier php suivant rassemble toutes les étapes décrites ci-dessus et produit le jeton résultant
touch jwt-token.php && chmod +x jwt-token.php && nano jwt-token.php
#!/usr/bin/php
<?php
// base64 encodes the header json
$encoded_header = base64_encode('{"alg": "HS256","typ": "JWT"}');
// base64 encodes the payload json
$encoded_payload = base64_encode('{"country": "France","name": "Ansel Arsenault","email": "AnselArsenault@jourrapide.com "}');
// base64 strings are concatenated to one that looks like this
$header_payload = $encoded_header . '.' . $encoded_payload;
//Setting the secret key
$secret_key = ']~5YL,ymj8Xe72';
//Clé secrète encodée base64
$secret_key = base64_encode($secret_key);
// Creating the signature, a hash with the s256 algorithm and the secret key. The signature is also base64 encoded.
$signature = base64_encode(hash_hmac('sha256', $header_payload, $secret_key, true));
// Creating the JWT token by concatenating the signature with the header and payload, that looks like this:
$jwt_token = $header_payload . '.' . $signature;
//listing the resulted JWT
echo $jwt_token;
?>
Exécution et sortie
./jwt-token.php
token
eyJhbGciOiAiSFMyNTYiLCJ0eXAiOiAiSldUIn0=.eyJjb3VudHJ5IjogIkZyYW5jZSIsIm5hbWUiOiAiQW5zZWwgQXJzZW5hdWx0IiwiZW1haWwiOiAiQW5zZWxBcnNlbmF1bHRAam91cnJhcGlkZS5jb20gIn0=.nGiEj+uzBf6eCtsFZCF0kbJUWdUDpg6SvA+zcPzNUPM=
Maintenant, si nous voulons vérifier que tout s’est bien passé, nous pouvons tester le jeton dans https://jwt.io/ en le collant dans la section Encodé. Vous verrez ensuite sur le côté droit, dans la section Décodage, toutes vos données. Mais pour que votre signature puisse le valider, vous devez entrer votre clé secrète au bas de la page, dans la section Vérifier la signature, comme vous pouvez le voir à partir de la figure suivante:
Vérification de la signature
Dans cette section, après avoir créé un jeton token en PHP, nous voulons vérifier qu’une signature reçue est correcte. La liste suivante obtient le jeton token, en extrait la signature, crée une nouvelle signature en utilisant l’en-tête header et la charge utile payload extraite du jeton reçu (en utilisant également la clé secrète $secret_key), puis comparez la signature. Si tout s’est bien passé, vous devriez obtenir un message de réussite:
Créer le php
touch jwt-verif.php && chmod +x jwt-verif.php && nano jwt-verif.php
#!/usr/bin/php
<?php
$recievedJwt = 'eyJhbGciOiAiSFMyNTYiLCJ0eXAiOiAiSldUIn0=.eyJjb3VudHJ5IjogIlJvbWFuaWEiLCJuYW1lIjogIk9jdGF2aWEgQW5naGVsIiwiZW1haWwiOiAib2N0YXZpYWFuZ2hlbEBnbWFpbC5jb20ifQ==.gbB+B063g+kwsoc4L3B1Bu2wM+VEBElwPiLOb0fj2SE=';
//Setting the secret key
$secret_key = ']~5YL,ymj8Xe72';
//Clé secrète encodée base64
$secret_key = base64_encode($secret_key);
// Split a string by '.'
$jwt_values = explode('.', $recievedJwt);
// extracting the signature from the original JWT
$recieved_signature = $jwt_values[2];
// concatenating the first two arguments of the $jwt_values array, representing the header and the payload
$recievedHeaderAndPayload = $jwt_values[0] . '.' . $jwt_values[1];
// creating the Base 64 encoded new signature generated by applying the HMAC method to the concatenated header and payload values
$resultedsignature = base64_encode(hash_hmac('sha256', $recievedHeaderAndPayload, $secret_key, true));
// checking if the created signature is equal to the received signature
if($resultedsignature == $recieved_signature) {
// If everything worked fine, if the signature is ok and the payload was not modified you should get a success message
echo "Success";
}
?>
Exécution et sortie
./jwt-verif.php
Success
Au cours de cet article, vous avez appris comment créer un jeton Web JSON en PHP, comment le tester et comment vérifier un jeton reçu s’il est correct. En conclusion, un jeton Web JSON est assez facile à créer et à valider vous-même. La clé secrète ne devra pas tomber dans de mauvaises mains, sinon vous ne pouvez plus faire confiance aux signatures.