PyJWT encodes and decodes JSON Web Tokens with jwt.encode() and jwt.decode(), and fetches signing keys from a JWKS endpoint with PyJWKClient. Asymmetric algorithms come from the crypto extra, which pulls in pyca/cryptography; with it, 2.15.1 handled the HS, RS, PS and ES families, ES256K and EdDSA, but not the newer Ed25519 and Ed448 names, which raised NotImplementedError: Algorithm not supported.
Its verification defaults are strict. jwt.decode() without algorithms fails with It is required that you pass in a value for the "algorithms" argument, an unsigned alg: none token never verified — not even with algorithms=["none"] — and a PEM or OpenSSH public key used as an HS256 secret was refused with InvalidKeyError. ES256 is bound to P-256: signing with a P-384 key failed with a curve mismatch.
What it leaves to you: exp is optional, so a token without one decodes forever unless you list it under require in the options argument, and leeway defaults to zero, so servers a few seconds apart already disagree about expiry. Short HMAC secrets are accepted with only an InsecureKeyLengthWarning, which is easy to miss in logs — test a secret with the JWT secret checker.
ES256 signatures are the 64-byte raw r‖s form JWS requires; a token whose signature was assembled as DER — for example from a KMS signing call — fails with the generic Signature verification failed. Paste the token into the JWT debugger to see the signature length, and convert with the DER ⇄ raw converter.
The sample pins the algorithm, issuer and audience, requires exp and allows 30 seconds of drift — the four settings that make jwt.decode() safe to call on tokens from another service.