Errors¶
ryjwt's exceptions fall into three groups:
| group | means | respond |
|---|---|---|
InvalidTokenError |
the token can't be trusted | 401 |
JWKSFetchError |
a JWKSClient has no keys to check tokens with |
503 |
InvalidKeyError |
a key you configured can't be used | fix the configuration |
All of them are RYJWTErrors:
RYJWTError
├── InvalidKeyError
├── JWKSFetchError
└── InvalidTokenError
├── DecodeError
│ └── InvalidSignatureError
├── InvalidAlgorithmError
├── UnknownKeyError
├── ExpiredSignatureError
├── ImmatureSignatureError
├── InvalidAudienceError
├── InvalidIssuerError
└── ClaimsValidationError
Rejected tokens¶
Every rejected token raises an InvalidTokenError, usually all you need to catch. Catch a
subclass to tell the reasons apart:
import secrets
import ryjwt
key = ryjwt.SecretKey(secrets.token_bytes(32), algorithms=["HS256"], audience="my-api")
try:
claims = key.decode("not-a-token")
except ryjwt.ExpiredSignatureError:
print("expired: get a new token")
except ryjwt.InvalidTokenError as e:
print(type(e).__name__) # DecodeError: respond 401
| exception | the token is rejected because |
|---|---|
DecodeError |
it's malformed: not three dot-separated base64url parts; header or payload not a JSON object; a header with a repeated field, over 64 fields, or a non-string kid (when the keys have kids); exp or nbf not a number |
InvalidSignatureError |
its signature doesn't match (a kind of DecodeError) |
InvalidAlgorithmError |
its alg is missing, not a string, or not in the key's algorithms. In a JWKS, the key it picked may also be limited to other algorithms |
UnknownKeyError |
its kid isn't in the JWKS; or the JWKS has several keys and it can't pick one (no kid, or the keys have none) |
ExpiredSignatureError |
it has expired (exp) |
ImmatureSignatureError |
it isn't valid yet (nbf) |
InvalidAudienceError |
its aud is missing, doesn't match audience, or isn't a string or list of strings; or it has an aud and you set no audience |
InvalidIssuerError |
you set issuer, and its iss is missing, doesn't match, or isn't a string |
ClaimsValidationError |
its claims don't fit your into class: a field is missing, of the wrong type or out of range. Its __cause__ is msgspec's or pydantic's ValidationError |
InvalidTokenError itself |
its header has a crit field, which ryjwt must reject |
The message gives details.
Keys you configured: InvalidKeyError¶
Raised when you create a key object from:
- a PEM that doesn't parse, holds no key or several, or the wrong kind (public for
PrivateKey, private forPublicKey); - a weak key: RSA outside 2048 to 8192 bits, or a small-order Ed25519 key;
- an HMAC secret that's empty, too short, or looks like a public key;
- a JWKS document that breaks the rules.
JWKS URLs: JWKSFetchError¶
A JWKSClient raises it when it has no usable keys because fetching failed. Not
the token's fault, so not an InvalidTokenError: respond 503.
Python's own exceptions¶
Calling ryjwt wrongly raises Python's own exceptions. A type checker catches most first.
ValueError:
algorithmsis empty, names one the class doesn't support, or mixes ones a key can't serve;- a key's
kidis empty; encodehas noalgorithmbut the key has several, or one the key doesn't have; or itsheadersetsalg,critorb64, setskidwhen the key has one, or has over 64 fields;jwkshas no keys, or several keys that don't each have their ownkid;jwkis called on aPublicKeyholding several keys (usejwks);- a
datetimeclaim has no timezone; leewayis negative or infinite;- a
JWKSClient's URL isn't allowed, or a time is negative.
TypeError:
intoisn't a msgspecStructor pydanticBaseModelclass;claimsisn't a dict,StructorBaseModel;tokenisn't astrorbytes;audienceorissuer(on a key, client ordecodecall) isn't astror iterable of them;leewayisn't a number or atimedelta;- a key's
kid, or one inencode'sheader, isn't astr; jwksis given something other than aPrivateKeyorPublicKey, such as aSecretKey.
OSError (FileNotFoundError, PermissionError, ...): from_path couldn't read the file.