Aller au contenu
ecdsa.com
Python

PyJWT

PyJWT de José Padilla, la bibliothèque JWT la plus utilisée en Python : une liste algorithms explicite est obligatoire, alg: none ne passe jamais la vérification et les clés publiques sont refusées comme secrets HMAC, mais l’expiration est facultative et la marge de tolérance est nulle.
Licence
MIT
Version vérifiée
2.15.1 ·
Code source
github.com/jpadilla/pyjwt
Documentation
pyjwt.readthedocs.io/en/stable/

Compatibilité en un coup d’œil

JOSE et JWT

Algorithmes JWS
HS256HS384HS512RS256RS384RS512PS256PS384PS512ES256ES384ES512ES256KEdDSA

PyJWT encode et décode des JSON Web Tokens avec jwt.encode() et jwt.decode(), et récupère les clés de signature depuis un point de terminaison JWKS avec PyJWKClient. Les algorithmes asymétriques viennent de l’extra crypto, qui installe pyca/cryptography ; avec lui, la 2.15.1 a géré les familles HS, RS, PS et ES, ES256K et EdDSA, mais pas les noms plus récents Ed25519 et Ed448, qui ont levé NotImplementedError: Algorithm not supported.

Ses valeurs par défaut de vérification sont strictes. jwt.decode() sans algorithms échoue avec It is required that you pass in a value for the "algorithms" argument, un jeton non signé alg: none n’a jamais passé la vérification, pas même avec algorithms=["none"], et une clé publique PEM ou OpenSSH utilisée comme secret HS256 a été refusée avec InvalidKeyError. ES256 est lié à P-256 : la signature avec une clé P-384 a échoué sur une non-concordance de courbe.

Ce qui reste à votre charge : exp est facultatif, si bien qu’un jeton qui n’en a pas se décode indéfiniment, sauf si vous le listez sous require dans l’argument options, et leeway vaut zéro par défaut, de sorte que des serveurs décalés de quelques secondes divergent déjà sur l’expiration. Les secrets HMAC courts sont acceptés avec un simple InsecureKeyLengthWarning, facile à manquer dans les journaux : testez un secret avec le vérificateur de secrets JWT.

Les signatures ES256 sont sous la forme r‖s brute de 64 octets qu’exige JWS ; un jeton dont la signature a été assemblée en DER (par exemple à partir d’un appel de signature à un KMS) échoue avec le message générique Signature verification failed. Collez le jeton dans le débogueur JWT pour voir la longueur de la signature, et convertissez-la avec le convertisseur DER ⇄ brut.

L’exemple fixe l’algorithme, l’émetteur et l’audience, exige exp et tolère 30 secondes de décalage : les quatre paramètres qui permettent d’appeler jwt.decode() sans risque sur des jetons provenant d’un autre service.

Les faits et les vérifications qui les fondent

Rien de ce qui suit n’est copié d’une documentation. La vérification content/libraries/verify/pyjwt/run.sh a exécuté PyJWT 2.15.1 sur cryptography 50.0.2 dans l’image de conteneur python:3.14.8-slim-trixie le 20 octobre 2026 ; la page doit correspondre à ce que la vérification a affiché, et chaque fait cite les lignes en question.

  • Algorithmes JWS

    HS256HS384HS512RS256RS384RS512PS256PS384PS512ES256ES384ES512ES256KEdDSA

    Signature et vérification réussies avec HS, RS, PS et ES en 256/384/512 bits, ES256K et EdDSA ; Ed25519 et Ed448 ont levé NotImplementedError: Algorithm not supported.

    Relevé par la vérification
    fact jws-algorithms = HS256, HS384, HS512, RS256, RS384, RS512, PS256, PS384, PS512, ES256, ES384, ES512, ES256K, EdDSA
    evidence jws-algorithms: Ed25519: NotImplementedError: Algorithm not supported
    evidence jws-algorithms: Ed448: NotImplementedError: Algorithm not supported
  • Encodage des signatures ES256

    raw

    Les signatures ES256 font 64 octets, en r‖s brut.

    Relevé par la vérification
    fact jws-ecdsa-encoding = raw
  • Signature DER dans un JWS

    rejected

    Un jeton avec une signature ES256 encodée en DER échoue avec InvalidSignatureError: Signature verification failed.

    Relevé par la vérification
    fact jws-der-signature = rejected
    evidence jws-der-signature: InvalidSignatureError: Signature verification failed
  • Liste d’algorithmes autorisés

    required

    jwt.decode() sans algorithms lève DecodeError: It is required that you pass in a value for the "algorithms" argument when calling decode().

    Relevé par la vérification
    fact jwt-algorithm-allowlist = required
    evidence jwt-algorithm-allowlist: DecodeError: It is required that you pass in a value for the "algorithms" argument when calling decode().
  • Jetons non signés (alg none)

    rejected

    Un jeton non signé est refusé avec The specified alg value is not allowed face à une clé ES256, et avec Signature verification failed même avec algorithms=["none"] ; seul verify_signature: False permet de le lire.

    Relevé par la vérification
    fact jwt-alg-none = rejected
    evidence jwt-alg-none: with an ES256 key: InvalidAlgorithmError: The specified alg value is not allowed
    evidence jwt-alg-none: algorithms ['none'] and no key: InvalidSignatureError: Signature verification failed
    evidence jwt-alg-none: verify_signature False: returned claims
  • Clé publique utilisée comme secret HMAC

    rejected

    Une clé publique PEM ou OpenSSH utilisée comme secret HS256 lève InvalidKeyError: The specified key is an asymmetric key or x509 certificate and should not be used as an HMAC secret.

    Relevé par la vérification
    fact jwt-hmac-public-key = rejected
    evidence jwt-hmac-public-key: PEM public key as HS256 secret: InvalidKeyError: The specified key is an asymmetric key or x509 certificate and should not be used as an HMAC secret.
    evidence jwt-hmac-public-key: OpenSSH public key as HS256 secret: InvalidKeyError: The specified key is an asymmetric key or x509 certificate and should not be used as an HMAC secret.
  • Courbe vérifiée par rapport à alg

    enforced

    Signer en ES256 avec une clé P-384 lève The key's curve 'secp384r1' does not match the expected curve 'secp256r1' for this algorithm.

    Relevé par la vérification
    fact jwt-ecdsa-curve-check = enforced
    evidence jwt-ecdsa-curve-check: InvalidKeyError: The key's curve 'secp384r1' does not match the expected curve 'secp256r1' for this algorithm
  • Décalage d’horloge toléré par défaut

    0s

    Aucune marge de tolérance par défaut : un jeton expiré depuis trois secondes a été rejeté.

    Relevé par la vérification
    fact jwt-clock-skew = 0s
  • Claim d’expiration obligatoire

    no

    Un jeton sans exp a été décodé avec succès ; rendez ce claim obligatoire en listant exp sous require dans l’argument options.

    Relevé par la vérification
    fact jwt-exp-required = no
  • Secret HMAC court

    accepted-with-warning

    Un secret HMAC de 5 octets a fonctionné pour la signature et la vérification, avec un InsecureKeyLengthWarning pour seul signal.

    Relevé par la vérification
    fact jwt-hmac-short-secret = accepted-with-warning
    evidence jwt-hmac-short-secret: 5-byte HMAC secret: accepted; warnings: InsecureKeyLengthWarning

Utilisation

Émettre un jeton ES256 et le décoder avec l’algorithme, l’émetteur et l’audience fixés, exp obligatoire et 30 secondes de tolérance.
import datetime

import jwt
from cryptography.hazmat.primitives.asymmetric import ec

key = ec.generate_private_key(ec.SECP256R1())
now = datetime.datetime.now(tz=datetime.timezone.utc)

token = jwt.encode(
    {"sub": "user-42", "iss": "https://auth.example.com", "aud": "api", "exp": now + datetime.timedelta(minutes=10)},
    key,
    algorithm="ES256",
)

# algorithms is mandatory; also require exp and allow a little clock drift.
claims = jwt.decode(
    token,
    key.public_key(),
    algorithms=["ES256"],
    audience="api",
    issuer="https://auth.example.com",
    leeway=30,
    options={"require": ["exp", "iss", "aud"]},
)
print("sub:", claims["sub"])

Le banc de test des extraits de code exécute chaque exemple tel quel dans l’image de conteneur python:3.14.8-slim-trixie, et l’exécution échoue si la sortie ne correspond pas.

Mise en place
pip install --quiet --root-user-action=ignore 'PyJWT[crypto]==2.15.1' cryptography==50.0.2
Commande
python usage.py
La sortie doit correspondre à cette expression régulière
sub: user-42

Bibliothèques