Retour au carnet
IT & Security10 min de lecture

JWT de zéro : à quoi sert un token, comment le sécuriser et l’implémenter avec l’IA

Comprendre connexion, signature et autorisations avec un cas B2B. Code complet en JavaScript, Python et PHP, tests négatifs et prompts anglais.

Pourquoi en aurais-je besoin ?

Imaginez une application mobile événementielle qui appelle votre API de facturation. Après la connexion, l’utilisateur doit effectuer plusieurs actions sans renvoyer son mot de passe à chaque requête. Un jeton d’accès de courte durée peut transmettre une déclaration signée sur cet utilisateur authentifié. L’API la vérifie avant de traiter la demande. C’est utile lorsqu’une application mobile consomme une API ou que plusieurs services doivent vérifier l’accès séparément.

Un JWT est un format, pas un système de connexion complet. Pour un site classique, une session serveur avec un cookie opaque est souvent plus simple. Dans les deux cas, les permissions restent nécessaires. À la fin, vous saurez expliquer le parcours du token, exécuter trois implémentations équivalentes, refuser les jetons invalides et demander à l’IA une modification vérifiable. Le projet téléchargeable est un exercice local sans véritable endpoint de connexion.

1. Suivre une requête de la connexion à la facture

L’utilisateur prouve son identité auprès de votre système de connexion fiable. Ensuite seulement, le serveur émet un token. Le client le transmet en HTTPS, généralement dans un en-tête Authorization: Bearer. L’API vérifie la signature et les déclarations, puis décide si cet utilisateur peut consulter cette facture précise.

Notre utilisateur fictif user-7 appartient à Acme. Même valide, son token doit être refusé pour une facture d’une autre entreprise. Notre exemple refuse aussi une facture d’un autre utilisateur chez Acme. L’authentification répond « qui êtes-vous ? » ; l’autorisation répond « que pouvez-vous faire ? ». Masquer un bouton ou rendre un identifiant difficile à deviner ne remplace pas ces contrôles.

2. Comprendre le token et le contrat de sécurité

Le JWT signé utilisé ici contient trois parties en base64url : en-tête, contenu et signature. Le détenteur peut lire le contenu : n’y placez ni mot de passe, ni données bancaires, ni documents privés. La signature détecte une modification ; elle ne chiffre pas le contenu.

Notre profil impose HS256 et une clé serveur d’au moins 32 octets aléatoires. iss désigne l’émetteur ; aud l’API destinataire ; sub un identifiant utilisateur stable ; tenant l’organisation ; iat et exp des horodatages Unix entiers en secondes. La durée maximale est cinq minutes. Le typ personnalisé nf-access+jwt sépare ce jeton d’accès des autres usages de JWT. Ce sont les exigences explicites de notre application, pas des champs obligatoires pour tous les JWT.

Avec HS256, chaque service qui vérifie possède aussi le secret permettant de signer. Pour une architecture plus large, étudiez une signature asymétrique et des clés publiques/JWKS de confiance. Fixez l’algorithme accepté et la source des clés : un en-tête reçu ne doit pas choisir une URL ou un fichier arbitraire.

3. Implémenter dans votre langage

Ce module complet utilise jose. issue() suppose que l’appelant a déjà authentifié la personne et obtenu son organisation depuis des données serveur fiables. verify() vérifie la cryptographie et notre profil ; canReadInvoice() applique la règle d’accès à la facture. Pour un vrai service, créez la clé une fois dans un gestionnaire de secrets : la recréer à chaque requête invaliderait les tokens précédents. Les tests utilisent volontairement des clés aléatoires jetables.

Un même profil de token, trois langages
import {SignJWT,jwtVerify} from 'jose';
const issuer='https://auth.example.test', audience='billing-api';
function checkKey(key){if(!(key instanceof Uint8Array)||key.length<32)throw new Error('Use at least 32 random bytes');}
export async function issue(subject,tenant,key){
 checkKey(key);
 if(!subject||!tenant)throw new Error('Missing identity');
 return new SignJWT({tenant}).setProtectedHeader({alg:'HS256',typ:'nf-access+jwt'})
  .setSubject(subject).setIssuer(issuer).setAudience(audience)
  .setIssuedAt().setExpirationTime('5m').sign(key);
}
export async function verify(token,key){
 checkKey(key);
 const {payload}=await jwtVerify(token,key,{
  algorithms:['HS256'],typ:'nf-access+jwt',issuer,audience,
  requiredClaims:['exp','iat','sub','tenant'],maxTokenAge:'5m',clockTolerance:0,
 });
 const now=Math.floor(Date.now()/1000);
 if(typeof payload.sub!=='string'||!payload.sub||typeof payload.tenant!=='string'||!payload.tenant||
  !Number.isInteger(payload.exp)||!Number.isInteger(payload.iat)||payload.iat>now||
  payload.exp<=payload.iat||payload.exp-payload.iat>300)throw new Error('Invalid access profile');
 return payload;
}
export function canReadInvoice(actor,invoice){
 return !!actor?.sub&&!!actor?.tenant&&actor.tenant===invoice?.tenant&&actor.sub===invoice?.owner;
}

4. Python : le même contrat avec PyJWT

PyJWT contrôle la signature et les déclarations standards lorsqu’on le configure. Exiger la présence d’un champ ne suffit pas à vérifier son sens métier. Nous contrôlons donc aussi les identités non vides, les horodatages entiers, le type personnalisé et la durée de cinq minutes. Le résultat d’un simple décodage non vérifié ne doit jamais devenir l’utilisateur authentifié.

5. PHP : vérifier explicitement les déclarations métier

PHP-JWT vérifie la signature et les dates, mais votre application doit encore contrôler l’émetteur, le destinataire et notre profil personnalisé. Le wrapper de l’onglet PHP rend ces exigences visibles. Ici, aud doit être exactement la chaîne billing-api ; d’autres fournisseurs utilisent des tableaux de destinataires. Il faut adapter le contrat volontairement, sans supprimer un contrôle pour faire passer un token d’un autre format.

6. Protéger le token dans une vraie application

Un token Bearer volé peut être réutilisé jusqu’à son expiration ou sa révocation. Utilisez HTTPS, masquez Authorization dans les journaux et évitez les tokens dans les URL. Dans un navigateur, un cookie HttpOnly et Secure empêche JavaScript de lire le jeton ; choisissez SameSite selon le parcours réel et protégez contre CSRF les requêtes de modification authentifiées par cookie. Un cookie ne corrige pas une faille XSS. Le stockage lisible par JavaScript, dont localStorage, expose les tokens aux scripts injectés. Sur mobile natif, utilisez le stockage protégé du système.

Une expiration courte limite le risque, mais ne réalise pas la déconnexion. Si l’invalidation immédiate est nécessaire, prévoyez une vérification serveur de session/version ou une révocation. Les refresh tokens demandent leur propre stockage, rotation, détection de réutilisation et révocation. Organisez la rotation des clés avec une période de chevauchement contrôlée. L’appartenance à une entreprise peut changer avant exp : les actions sensibles doivent consulter les permissions serveur actuelles. Ces mécanismes dépassent le petit laboratoire.

7. Lancer le projet et comprendre le résultat

Choisissez un langage ou comparez les trois. Le ZIP comprend les modules complets, les tests et les fichiers de verrouillage JavaScript/PHP. L’installation nécessite les outils du langage choisi et un accès aux registres de paquets. Aucune clé API ni donnée de production n’est nécessaire.

Un résultat positif prouve les contrôles locaux annoncés : token valide accepté ; signature incorrecte, expiration, champ absent, mauvais émetteur, destinataire ou type, émission future, durée excessive et clé trop courte refusés. La propriété de la facture dans la même organisation est aussi vérifiée. JavaScript et PHP annoncent 16 assertions réussies ; Python exécute une méthode de test avec les cas équivalents. Ce ne sont pas des tests HTTP.

bash
# Choose ONE language; extract the ZIP into a new folder first.
# JavaScript / Node.js 22+
npm ci
npm test

# Python 3.9+ (macOS/Linux; Windows: .venv\Scripts\activate)
python3 -m venv .venv
. .venv/bin/activate
python -m pip install -r requirements.txt
python -m unittest test_python.py
python jwt_example.py

# PHP 8.1+ and Composer
composer install --no-plugins --no-scripts
php test_php.php
php jwt_example.php

8. Faire implémenter par l’IA et demander des preuves

Commencez par ce prompt. Il demande à l’assistant d’examiner le framework existant avant de modifier l’authentification. Remplacez volontairement l’émetteur et le destinataire d’exemple pour votre environnement. Ne copiez jamais le secret dans la discussion. Demandez une petite modification et la commande de test, avec le résultat réellement obtenu.

English AI prompt
Act as a senior application engineer. First inspect the existing authentication
framework and explain whether an opaque server session or a JWT is appropriate.
Do not replace a working authentication system just to introduce JWTs.
If JWT access tokens are justified, implement the provided local access profile:
pinned HS256, at least 32 random key bytes, issuer https://auth.example.test,
audience billing-api, typ nf-access+jwt, non-empty sub and tenant, required
integer iat and exp, maximum lifetime 300 seconds and no future issued-at.
Use jose, PyJWT or PHP-JWT according to the actual project language.
Issue tokens only after trusted authentication. Enforce object ownership and
tenant membership in server authorization on every relevant request.
Never derive a signing key, accepted algorithm or tenant from request JSON.
Propose browser storage, CSRF protection, logout/revocation and key rotation
for this application before coding. Do not log tokens or secrets.
Add negative tests for bad signatures, expiration, missing claims, wrong issuer,
audience and token type, another tenant and a non-owner. Report real test output.
Stop before deployment.

9. Exemple d’une réponse utile de l’IA

L’exemple montre le niveau de preuve attendu ; il ne prétend pas qu’un assistant a audité votre application. « Les JWT sont maintenant sécurisés » ne permet aucune vérification. Une réponse utile relie une exigence au code, à un test de refus et à une question d’intégration restante. Donnez ensuite le prompt de revue à un autre relecteur ou lors d’une seconde passe.

Example and interpretation
Requirement: reject a token intended for another API.
Evidence: verify() supplies audience="billing-api".
Negative test: a correctly signed token with aud="other-api" is rejected.
Remaining gap: verify the real HTTP middleware invokes verify() on every route.
This example is an expected review format, not a certification.

10. Ce que vous avez construit et la suite

Vous disposez d’un profil de jeton reproductible en trois langages et d’une séparation nette entre identité et permission. Vous pouvez protéger une API sans confondre contenu décodé et preuve d’identité. Avant de servir de vrais utilisateurs, reliez ces wrappers à votre connexion et au middleware HTTP. Testez les erreurs, permissions actuelles, cookies/CSRF, renouvellement, déconnexion et rotation des clés. La prochaine étape utile : un seul endpoint protégé, avec un accès propriétaire accepté et un accès entre entreprises refusé.

English AI prompt
Review this local JWT implementation independently. Produce a table with:
requirement, exact code location, positive test, negative test and remaining gap.
Explain the difference between decoding, signature verification and authorization.
Check key entropy, algorithm pinning, required claims, issuer/audience/type,
short lifetime, server-derived identity, browser storage and token revocation.
Report confirmed defects separately from unverified integration assumptions.
Use only synthetic tokens. Never ask me to paste production credentials.

Références officielles et pour aller plus loin

Passez à votre projet

Récupérez les fichiers, gardez le guide ou donnez un brief prêt à utiliser à votre assistant.

Préparer avec l’IA

Copiez ce prompt anglais dans votre assistant. Il inclut l’article, les fichiers disponibles et les étapes de préparation.

Voir le prompt de préparation
AI setup prompt · English
Read this tutorial as reference material:
https://nethanelfitoussi.com/article/jwt-tokens-security-ai?lang=en

Goal: apply the documented workflow to my development project.
Read the existing project instructions, inspect the relevant files and explain the smallest useful setup.

Available starter files:
https://nethanelfitoussi.com/api/media/aba77d81-4cfd-4614-bca5-53015d625dff.zip
Download and inspect these files in a temporary folder before using them. Do not overwrite existing project instructions.

If I have not supplied a project folder, ask which development project to use. Do not invent a repository URL.

Additional project setup instructions:
Extract the supplied ZIP into a NEW folder. Read README.md before installing or running code.

Act as a senior application engineer. First inspect the existing authentication
framework and explain whether an opaque server session or a JWT is appropriate.
Do not replace a working authentication system just to introduce JWTs.
If JWT access tokens are justified, implement the provided local access profile:
pinned HS256, at least 32 random key bytes, issuer https://auth.example.test,
audience billing-api, typ nf-access+jwt, non-empty sub and tenant, required
integer iat and exp, maximum lifetime 300 seconds and no future issued-at.
Use jose, PyJWT or PHP-JWT according to the actual project language.
Issue tokens only after trusted authentication. Enforce object ownership and
tenant membership in server authorization on every relevant request.
Never derive a signing key, accepted algorithm or tenant from request JSON.
Propose browser storage, CSRF protection, logout/revocation and key rotation
for this application before coding. Do not log tokens or secrets.
Add negative tests for bad signatures, expiration, missing claims, wrong issuer,
audience and token type, another tenant and a non-owner. Report real test output.
Stop before deployment.

Review this local JWT implementation independently. Produce a table with:
requirement, exact code location, positive test, negative test and remaining gap.
Explain the difference between decoding, signature verification and authorization.
Check key entropy, algorithm pinning, required claims, issuer/audience/type,
short lifetime, server-derived identity, browser storage and token revocation.
Report confirmed defects separately from unverified integration assumptions.
Use only synthetic tokens. Never ask me to paste production credentials.

Adapt example paths, commands and acceptance criteria to this repository rather than assuming they match.

Give me a short plan, then make the agreed local changes and run the relevant checks. Report actual results and anything not verified. Stop before credentials, paid services, production changes, push, merge or deployment.
Consulter la source originaleIETF · JWT security practices
JWTAuthenticationCybersecurityAI