Aller au contenu
English

Flux d'application

  • Leçon 5
  • 55 min
  • Module 2 : Comprendre la connexion et les jetons

Ce que vous saurez faire

Connectez-vous étape par étape avec le flux de code d’autorisation et PKCE, le flux utilisé par toutes les applications modernes. Voyez pourquoi le code qui passe par le navigateur est inutile à un voleur. Donnez ensuite une identité propre à un service en arrière-plan, distincte de toute personne.

Avant de commencerCe qu’il faut savoir et avoir sous la mainEnviron 55 minutes

Ce que vous devez savoir

Ce qu’il vous faut sur votre ordinateur

  • Le projet après OAuth et OIDC : le dossier idp/, PyJWT installé et python -m idp init exécuté.
  • Le fournisseur en cours d’exécution dans son propre terminal : python -m idp.

Cette leçon ajoute un fichier, scripts/login.py. Toutes les personnes et tous les jetons sont fictifs, fournis par le fournisseur d’exercice sur votre ordinateur. Le tout a été testé sur macOS (Apple silicon); les commandes Windows sont écrites, mais non testées.

Vous laissez un complet chez le nettoyeur. Vous recevez un reçu numéroté, et le nettoyeur vous demande les quatre derniers chiffres de votre numéro de téléphone. Une semaine plus tard, vous présentez le reçu et dites les quatre chiffres. Vous récupérez le complet.

Le reçu seul ne suffit pas, et c’est voulu. Il peut tomber de votre poche, et n’importe qui peut lire son numéro. Un inconnu qui le trouve ne connaît pas vos quatre chiffres. Il ne reçoit donc rien.

Deux éléments rendent la récupération sûre : un reçu que tout le monde peut voir, et un secret que vous seul connaissez. Vous avez donné une information sur le secret au départ. Le nettoyeur peut donc le vérifier à la fin.

Le problème : l’adresse du navigateur n’est pas un endroit sûr

Section intitulée « Le problème : l’adresse du navigateur n’est pas un endroit sûr »

À la fin de OAuth et OIDC, vous avez ouvert la page de connexion du fournisseur. Si vous choisissez Sam sur cette page, le fournisseur envoie le navigateur à une nouvelle adresse. Le navigateur affiche quelque chose comme ceci dans sa barre d’adresse :

http://127.0.0.1:8765/callback?code=YbO09uJlww7dMqrtMNfQSaEss-zZ_Iy-4ZPnsmdpQfE&state=practice

La page ne se charge pas, car aucun programme n’écoute actuellement sur le port 8765. Ce n’est pas un problème. Regardez plutôt l’adresse : elle contient la réponse du fournisseur. Une adresse n’est pas privée. Le navigateur la conserve dans son historique, et un serveur peut l’écrire dans son journal. Sur un téléphone, une autre application peut enregistrer le même type d’adresse et la recevoir.

Un ancien flux OAuth pour les applications de navigateur, le flux implicite, plaçait le jeton d’accès lui-même dans cette adresse. Toute personne qui voyait l’adresse pouvait appeler l’API en tant que Sam. L’adresse ci-dessus ne contient qu’un code. Mais si ce code suffisait à obtenir des jetons, le problème resterait le même.

L’idée : un code dans le navigateur, des jetons sur un canal distinct

Section intitulée « L’idée : un code dans le navigateur, des jetons sur un canal distinct »

Le flux de code d’autorisation répartit la connexion entre deux canaux. Le canal du navigateur est le reçu papier : tout le monde peut le voir. Il ne transporte donc qu’un code court à usage unique. Le second canal est une requête directe de l’application au fournisseur : POST /token. Cette requête échange le code contre les jetons, et le navigateur ne la voit jamais.

PKCE (Proof Key for Code Exchange, prononcé « pixie ») représente les quatre chiffres du téléphone. Avant la connexion, l’application crée un secret aléatoire, le vérificateur de code, et le conserve. Elle envoie seulement une empreinte du vérificateur, le défi de code, avec la première redirection. Lors de l’échange, elle envoie le vérificateur lui-même. Le fournisseur calcule de nouveau l’empreinte et compare les deux. Un voleur qui a copié le code dans la barre d’adresse n’a pas le vérificateur. L’échange échoue donc.

L’empreinte est SHA-256, une fonction de hachage. Elle transforme n’importe quel texte en 32 octets. Le même texte donne toujours les mêmes 32 octets, mais personne ne peut retrouver le texte à partir des octets. Voici un petit exemple où toutes les valeurs sont visibles :

Étape Valeur
Vérificateur de code (conservé par l’application) larkfield-practice-verifier-0123456789-abcdefgh (47 caractères)
Son SHA-256 (32 octets, les 8 premiers en hexadécimal) 50593c8650b2db63…
Défi de code = base64url des 32 octets (envoyé en premier) UFk8hlCy22MX4XcN9PO8q1bTpbNNi-dHh307J7UAFRA
Le vérificateur dont la dernière lettre est remplacée par i défi U4VI_lRYy3zDRt5RJYomuX04X3LF2_TwdCA08Cof4X0

Une seule lettre modifiée donne un défi complètement différent. Base64url écrit les octets avec des lettres, des chiffres, - et _, qui conviennent à une adresse. Lire un jeton : JWT, signatures et clés montre comment. Vérifiez vous-même le défi :

Fenêtre de terminal
python -c "import base64, hashlib; v = 'larkfield-practice-verifier-0123456789-abcdefgh'; print(base64.urlsafe_b64encode(hashlib.sha256(v.encode()).digest()).decode().rstrip('='))"
UFk8hlCy22MX4XcN9PO8q1bTpbNNi-dHh307J7UAFRA

Un vrai vérificateur est aléatoire, pas une phrase : le projet crée 64 caractères aléatoires. La norme permet de 43 à 128 caractères.

Deux autres valeurs protègent le flux. state (état) est une valeur aléatoire que l’application envoie et s’attend à recevoir sans changement. Un retour avec un autre état n’a pas commencé dans cette application, alors l’application s’arrête. Cela bloque un attaquant qui tente de connecter la personne à un compte qu’elle n’a pas choisi. nonce (valeur à usage unique) est une valeur aléatoire que le fournisseur copie dans le jeton d’identité. L’application la vérifie. Un ancien jeton d’identité ne peut donc pas être réutilisé dans une nouvelle connexion.

Voici le flux complet, tel que la connexion par navigateur du module 3 l’exécutera. Le serveur dorsal de l’API de billets joue le rôle de l’application :

Une séquence avec trois participants : le navigateur avec Sam, ticket-api comme serveur dorsal et le fournisseur d'identité. Le navigateur demande /auth/login. L'API crée state, un nonce et une paire PKCE. Le vérificateur de code reste secret sur le serveur, et le défi de code est le SHA-256 du vérificateur. L'API répond par une redirection vers le fournisseur, avec client_id, redirect_uri, scope, state et code_challenge. Le navigateur ouvre /authorize, et le fournisseur affiche sa page de connexion. Sam se connecte; seul le fournisseur voit cette étape. Le fournisseur redirige le navigateur vers /auth/callback avec un code et l'état. L'API vérifie que l'état est le sien. Elle envoie ensuite un POST à /token avec le code, le vérificateur de code et son secret client. Le fournisseur vérifie que le SHA-256 du vérificateur correspond au défi. Il vérifie aussi que le code est inutilisé et date de moins de 60 secondes. Il répond avec trois jetons : un jeton d'identité pour l'application, un jeton d'accès pour ticket-api valable 10 minutes et un jeton de renouvellement pour obtenir d'autres jetons plus tard. L'API vérifie le nonce du jeton d'identité et conserve les jetons sur le serveur. Le module 3 ajoute un témoin de session. Une application en ligne de commande obtient les mêmes jetons et envoie Authorization : Bearer avec le jeton d'accès. Le navigateur demande /v1/me. L'API vérifie la signature, l'émetteur, l'audience et l'expiration du jeton d'accès. Elle répond 200 avec usr-sam, membre du personnel de Larkfield. Après 10 minutes, le jeton de renouvellement obtient de nouveaux jetons sans le navigateur.
Le flux de code d'autorisation avec PKCE, où chaque jeton est identifié. Le navigateur ne transporte que le code; les jetons passent par le canal direct entre l'API et le fournisseur.
Description textuelle du diagramme

Suivez la ligne du navigateur à gauche. Il voit le défi, la page de connexion, le code et l’état, et rien d’autre. Le vérificateur reste dans le serveur dorsal. Les trois jetons circulent seulement entre le fournisseur et le serveur dorsal, dans la réponse à POST /token.

Chaque jeton a un rôle. Le jeton d’identité indique à l’application qui s’est connecté. Le jeton d’accès va à l’API, pour 10 minutes. Le jeton de renouvellement retourne uniquement au fournisseur pour obtenir de nouveaux jetons quand le jeton d’accès expire. Dans ce module, scripts.login joue à la fois le navigateur et l’application, pour que vous puissiez voir chaque étape. Le module 3 intègre la partie navigateur à l’API.

Larkfield : se connecter en tant que Sam, étape par étape

Section intitulée « Larkfield : se connecter en tant que Sam, étape par étape »
  1. Dans le second terminal (dossier du projet, environnement virtuel actif), copiez le script de connexion depuis l’instantané :

    Fenêtre de terminal
    cp ../nextia-learning-labs/C23/snapshots/end-of-m02/scripts/login.py scripts/
  2. Vérifiez que le fournisseur fonctionne toujours dans le premier terminal. Connectez-vous ensuite en tant que Sam :

    Fenêtre de terminal
    python -m scripts.login --user usr-sam
  3. Lisez la sortie depuis le début. Elle contient trois étapes numérotées, puis les deux jetons décodés.

Voici la sortie de l’auteur, en trois parties. D’abord, la connexion :

1. The application makes a PKCE pair and sends you to the provider's sign-in page:
code_verifier ZOeRGN_qSkOP...(64 characters) (stays in the application)
code_challenge BpOUbcrGHcdQlpwHi4hyZo46UgsphM1Q7TUNZhpeWCE (SHA-256 of the verifier, sent now)
GET http://localhost:8400/authorize?response_type=code&client_id=ticket-cli&redirect_uri=http%3A%2F%2F127.0.0.1%3A8765%2Fcallb... -> 200
2. You pick usr-sam on the page. The provider sends the browser back with a code:
302 Location: http://127.0.0.1:8765/callback?code=UluH440p1gDlIUNxgQ7-WhEtPZ5I-zFgk3...
The state matches the one we sent.
3. The application swaps the code and the verifier for tokens (POST /token):
200: token_type Bearer, expires_in 600 s,
access_token eyJhbGciOiJSUzI1NiIsImtp...(732 characters)
id_token eyJhbGciOiJSUzI1NiIsImtp...(712 characters)
refresh_token NlyX75NN...(43 characters)

L’étape 1 est l’empreinte : le défi circule, le vérificateur de 64 caractères reste sur place. L’étape 2 est le reçu papier : une redirection 302 avec le code dans l’adresse, et le script vérifie l’état. L’étape 3 est le canal direct : un POST /token et trois jetons. Le jeton d’accès expire dans 600 secondes, soit 10 minutes.

Ensuite, les deux jetons décodés. Le script affiche leur contenu, mais il ne les vérifie pas. La vérification revient à l’API, dans Ce que signifie un jeton.

id_token (decoded, not checked):
header {"alg": "RS256", "kid": "7HxcBtk4djdrsPzB", "typ": "JWT"}
claims {
"iss": "http://localhost:8400",
"sub": "usr-sam",
"aud": "ticket-cli",
"exp": 1791611579,
"iat": 1791610979,
"auth_time": 1791610979,
"name": "Sam",
"email": "sam@larkfield.example",
"email_verified": true,
"nonce": "RE-SmzqRfOzTdnM6KCALNQ"
}
access_token (decoded, not checked):
header {"alg": "RS256", "kid": "7HxcBtk4djdrsPzB", "typ": "at+jwt"}
claims {
"iss": "http://localhost:8400",
"sub": "usr-sam",
"aud": "ticket-api",
"exp": 1791611579,
"nbf": 1791610979,
"iat": 1791610979,
"jti": "cd296cf053a04ee2be30c7823f5b9a52",
"client_id": "ticket-cli",
"scope": "tickets:read tickets:write members:manage"
}
Saved in .tokens/usr-sam.json.

Le kid dans l’en-tête est l’identifiant de la clé qui a signé le jeton. Cette sortie vient d’une autre exécution de l’auteur. Son kid diffère donc de celui affiché par idp init dans la dernière leçon. Le vôtre est l’identifiant de votre propre clé.

Comparez les deux jetons ligne par ligne. Les deux indiquent "sub": "usr-sam" : la même personne. Mais leur aud (audience) diffère. Le jeton d’identité est destiné à ticket-cli, l’application. Le jeton d’accès est destiné à ticket-api. Le jeton d’identité contient le nom de la personne, son courriel et le nonce. Le jeton d’accès contient plutôt l’application (client_id) et la scope (portée). Aucun des deux ne nomme une organisation ou un rôle. Leurs en-têtes diffèrent aussi : typ (type) vaut JWT pour le jeton d’identité et at+jwt pour le jeton d’accès. Lire un jeton : JWT, signatures et clés explique chaque champ.

Les jetons sont enregistrés dans .tokens/usr-sam.json. Git ignore ce dossier, car toute personne qui copie un jeton enregistré peut l’utiliser jusqu’à son expiration.

Jouez maintenant le rôle du voleur. Le script peut envoyer un mauvais vérificateur : un nouveau vérificateur aléatoire, comme un voleur qui tenterait de le deviner :

Fenêtre de terminal
python -m scripts.login --user usr-sam --wrong-verifier
3. The application swaps the code and the verifier for tokens (POST /token):
400 {'error': 'invalid_grant', 'error_description': 'The PKCE code_verifier does not match the code_challenge.'}

Le code était réel et récent, mais sans le vérificateur, il n’a rien donné. Le fournisseur permet aussi d’utiliser un code une seule fois, et seulement pendant 60 secondes. Même la véritable application ne peut pas l’utiliser deux fois. Le test test_a_code_works_once le vérifie; À votre tour vous permet de le voir.

La connexion de Sam est une façon d’appeler l’API. Le projet contient trois applications enregistrées, appelées clients dans OAuth. Chacune prouve son identité à sa façon :

Trois colonnes. Connexion par navigateur : le client help-desk-web est un client confidentiel, un serveur dorsal qui conserve un secret. Il utilise le code d'autorisation avec PKCE et son secret client. Ses jetons indiquent sub usr-sam : une personne. Ligne de commande : le client ticket-cli est un client public qui fonctionne sur votre ordinateur et n'a aucun secret. Il utilise le code d'autorisation avec PKCE; PKCE est donc sa seule preuve. Ses jetons indiquent sub usr-sam : une personne. Service en arrière-plan : le client export-worker est un client confidentiel sans personne derrière lui. Il utilise les identifiants client, son propre identifiant et son secret. Son jeton indique sub export-worker : un service, avec la portée tickets:read.
Les trois clients du projet. Deux agissent pour une personne; le service en arrière-plan agit en son propre nom.
Description textuelle du diagramme

Deux mots définissent chaque colonne. Un client confidentiel fonctionne là où personne d’autre ne peut lire ses fichiers, par exemple sur un serveur. Il peut donc conserver un secret client. Un client public fonctionne sur l’ordinateur ou le téléphone de la personne, où n’importe qui peut lire le programme. Il n’a aucun secret, alors PKCE est sa seule preuve. La troisième colonne ne comporte aucune personne. Le service demande un jeton avec son propre identifiant et son secret, un flux appelé identifiants client.

Obtenez le jeton propre au service en arrière-plan :

Fenêtre de terminal
python -m scripts.login --service
access_token (decoded, not checked):
header {"alg": "RS256", "kid": "My9O_SxNpIg4Ff10", "typ": "at+jwt"}
claims {
"iss": "http://localhost:8400",
"sub": "export-worker",
"aud": "ticket-api",
"exp": 1791613197,
"nbf": 1791612597,
"iat": 1791612597,
"jti": "d75fcc850a9d4cf9a2b22246768f8b80",
"client_id": "export-worker",
"scope": "tickets:read"
}
Saved in .tokens/export-worker.json.

Il n’y a ni jeton d’identité ni jeton de renouvellement : personne ne s’est connecté. Le sub (sujet) est le service lui-même, identique à client_id, et sa seule portée est tickets:read. Votre identifiant de clé diffère de celui de l’auteur, car init a créé votre clé.

« Pourquoi ne pas donner le jeton de Sam à la tâche d’exportation? » demande Amira. Kwame répond en pensant à l’audit. Une tâche exécutée en tant que Sam apparaît comme Sam dans chaque journal. Elle s’arrête quand le jeton de Sam expire et conserve les droits de Sam après son départ de la boutique. Gardez donc les identités distinctes : le jeton d’une personne pour ses actions actuelles, et le jeton d’un service pour ce service. Contexte d’identité des tâches ajoute la personne pour qui une tâche s’exécute.

Jetons de renouvellement : de nouveaux jetons sans nouvelle connexion

Section intitulée « Jetons de renouvellement : de nouveaux jetons sans nouvelle connexion »

Un jeton d’accès dure 10 minutes, alors un vol de courte durée cause peu de tort. Mais personne ne veut se connecter toutes les 10 minutes. Le jeton de renouvellement résout ce problème : l’application l’envoie au fournisseur et obtient de nouveaux jetons. La personne ne fait rien. Le fournisseur d’exercice effectue aussi une rotation des jetons de renouvellement. Chacun fonctionne une seule fois, et la réponse en contient un nouveau.

Fenêtre de terminal
python -m scripts.login --refresh usr-sam
New tokens. The old refresh token cannot be used again (rotation).

Les nouveaux jetons suivent, décodés, comme lors de la connexion ci-dessus.

La rotation transforme un jeton de renouvellement volé en alarme. Si un ancien jeton de renouvellement revient, deux parties en ont une copie. Le fournisseur ne peut pas savoir laquelle est le voleur. Il met donc fin à toute la famille de jetons de cette connexion. Essayez : gardez une copie des jetons enregistrés de Sam, puis renouvelez-les une fois. Remettez l’ancienne copie et renouvelez encore avec le jeton de renouvellement déjà utilisé.

Fenêtre de terminal
cp .tokens/usr-sam.json old-sam.json
python -m scripts.login --refresh usr-sam
cp old-sam.json .tokens/usr-sam.json
python -m scripts.login --refresh usr-sam

Le premier renouvellement affiche les nouveaux jetons. Le second, avec le jeton de renouvellement déjà utilisé, reçoit :

400 {'error': 'invalid_grant', 'error_description': 'The refresh token was already used or revoked.'}

Supprimez ensuite old-sam.json et reconnectez-vous (python -m scripts.login --user usr-sam). Le fournisseur a mis fin aux jetons de l’ancienne connexion.

Après cela, le jeton de renouvellement le plus récent de la même connexion échoue aussi. Le test test_a_refresh_token_works_once_and_its_reuse_ends_the_family le vérifie. Le module 3 conserve le jeton de renouvellement sur le serveur, où le navigateur ne peut pas y accéder.

  • Code d’autorisation avec PKCE : pour chaque connexion d’une personne, dans un navigateur, sur un téléphone ou en ligne de commande. Utilisez PKCE même avec un secret client; les normes le recommandent maintenant pour tous les clients.
  • Identifiants client : pour un service sans personne, comme le processus d’exportation. Jamais pour une action qu’une personne a demandée « tout de suite » : cette action exige l’identité de la personne.
  • Jetons de renouvellement : quand la connexion doit durer plus longtemps qu’un jeton d’accès. Conservez-les là où seule l’application peut les lire.

Deux anciens flux ne sont plus recommandés. Le flux implicite plaçait le jeton d’accès dans l’adresse. Dans le flux mot de passe, l’application demandait elle-même le mot de passe. Ne les utilisez pas dans de nouveaux projets.

Les coûts : chaque connexion exige une redirection et deux allers-retours. Chaque client confidentiel a un secret à protéger, et chaque application a besoin d’une adresse de redirection enregistrée. Chacun de ces paramètres peut être incorrect. Configuration du fournisseur les vérifie.

Faites d’abord une prédiction : le script échange le code contre des jetons, puis renvoie ce code avec le bon vérificateur. Que se passe-t-il à la deuxième requête?

Fenêtre de terminal
python -m scripts.login --user usr-sam --reuse-code
Afficher la réponse
3. The application swaps the code and the verifier for tokens (POST /token):
200: tokens. Now the same code again:
400 {'error': 'invalid_grant', 'error_description': 'The code is not valid, was used, or expired.'}

Le premier échange obtient les jetons. Le deuxième reçoit invalid_grant (autorisation invalide), même si le code et le vérificateur sont corrects. Un code ressemble au reçu du nettoyeur : le nettoyeur le garde quand vous récupérez vos vêtements. Un code divulgué après la connexion ne vaut donc rien. Un code divulgué avant la connexion reste valide au plus 60 secondes.

Erreur courante

Une adresse de redirection non enregistrée

Symptôme : le navigateur affiche une courte erreur du fournisseur au lieu de la page de connexion :

{"error":"invalid_request","error_description":"This redirect URI is not registered for the application."}

Cause : le fournisseur envoie des codes seulement aux adresses que l’application a enregistrées auparavant. Ici, la requête indiquait redirect_uri=https://evil.example/cb au lieu de http://127.0.0.1:8765/callback. Une différence plus petite produit la même erreur : localhost au lieu de 127.0.0.1, un autre port ou un / final. Le fournisseur exige des adresses exactement identiques.

Diagnostic : comparez le redirect_uri (adresse de redirection) dans l’adresse avec la liste des clients dans idp/store.py (CLIENTS). Pour un vrai fournisseur, comparez-le avec la page d’enregistrement de l’application.

Correction :

  1. Utilisez exactement l’adresse enregistrée dans l’application, ou enregistrez la nouvelle adresse auprès du fournisseur.
  2. N’enregistrez jamais un motif comme « toute adresse sur ce domaine ». Une liste exacte assure la protection.

Le fournisseur répond avec une page d’erreur, pas une redirection. S’il redirigeait vers une adresse inconnue avec une erreur, un attaquant pourrait l’utiliser pour envoyer les gens n’importe où. Le test test_an_unregistered_redirect_uri_is_an_error_page_not_a_redirect vérifie ce comportement.

Vérifiez votre compréhension

Retracez la connexion de Sam dans la sortie ci-dessus. Quelles valeurs passent par le navigateur? Lesquelles circulent seulement entre l'application et le fournisseur?

Afficher la réponse

Par le navigateur, dans la redirection vers /authorize : le défi de code, l’identifiant du client, l’adresse de redirection, les portées et l’état. Dans la redirection de retour : le code et l’état. Seulement entre l’application et le fournisseur : le vérificateur de code, dans la requête POST vers /token, et les trois jetons dans sa réponse. C’est le but de ce flux : les valeurs transportées par le navigateur sont inutiles à elles seules.

Vérifiez votre compréhension

Une application mobile n'a pas de secret client. Qu'est-ce qui empêche une autre application du même téléphone d'utiliser un code intercepté?

Afficher la réponse

PKCE. La vraie application a créé le vérificateur de code et envoyé seulement son empreinte SHA-256, le défi. Pour échanger le code, l’autre application a besoin du vérificateur lui-même, qui n’a jamais quitté la vraie application. Le fournisseur calcule l’empreinte de ce qu’il reçoit et refuse toute différence avec invalid_grant, comme le montre l’exécution avec –wrong-verifier.

Vérifiez votre compréhension

La tâche d'exportation nocturne s'exécute à 2 h. Doit-elle utiliser le jeton d'accès de la personne qui l'a planifiée, ou son propre jeton obtenu par les identifiants client? Quels problèmes poserait le jeton de cette personne?

Afficher la réponse

Son propre jeton, obtenu par les identifiants client, avec seulement la portée nécessaire (tickets:read). Le jeton d’accès d’une personne expire après 10 minutes. La tâche échouerait donc ou aurait besoin du jeton de renouvellement de cette personne. Chaque journal indiquerait la personne, pas la tâche. Et la tâche garderait les droits de cette personne après son départ. La personne pour qui elle s’exécute est enregistrée séparément et vérifiée à l’exécution de la tâche (module 5).

Récapitulatif

  • Le flux de code d’autorisation envoie seulement un code court à usage unique par le navigateur. L’application l’échange contre des jetons par une connexion directe (POST /token).
  • PKCE : l’application garde un vérificateur de code aléatoire et envoie son empreinte SHA-256, le défi de code. Un voleur qui possède le code sans le vérificateur reçoit invalid_grant.
  • state (état) protège l’application contre un rappel qu’elle n’a pas lancé; le nonce lie le jeton d’identité à cette connexion.
  • Une connexion a fourni trois jetons. Le jeton d’identité est destiné à l’application (aud ticket-cli). Le jeton d’accès est destiné à l’API (aud ticket-api, 600 s), et le jeton de renouvellement, au fournisseur.
  • Trois clients, trois identités : help-desk-web (confidentiel), ticket-cli (public, PKCE seulement) et export-worker (identifiants client, sub = lui-même).
  • Les jetons de renouvellement font l’objet d’une rotation; réutiliser un jeton invalide toute sa famille.
  • Ensuite : ouvrez un jeton et voyez ce que sa signature prouve.

Leçon suivante : Lire un jeton : JWT, signatures et clés