Originally posted by **HarshitDoshi** April 10, 2023
Hello,
I have been exploring the Authlib package since a couple of days now. I am trying to replicate the usual JWT authentication flow and experimenting with the `encode()` and `decode()` functions of a `JsonWebToken` from `authlib.jose`.
The encoding works as expected and am able to generate encoded JWTs with ease, thanks to the great documentation.
I am facing issues with the decoding part of it. Especially, the validation part of it. I am using the `validate()` function for that. I keep seeing the following exception being caught:
```
invalid_claim: Invalid claim "exp"
```
Upon inspection, I found out that the `validate_exp()` functoin that I use in `claims_options` is checking for the type of the "exp" claim in the `_validate_numeric_time()` function and it is specifically checking for the "exp" claim to be of int or float type. This is fine and expected, but the `JWTClaims` class instance is resulting in the decoded JWT to have all keys and values of type str and not maintaining the float type of the timestamp of "exp" and other time-related claims. This is causing the above exception to be raised.
I am posting below some of the code I have tried so that it can be replicated if needed:
```python
from typing import Any, List, Literal, Optional, Self, Union
from authlib.jose import JsonWebToken, JWTClaims
from authlib.jose.errors import InvalidClaimError
jwt = JsonWebToken(["RS256"])
private_pem_file = open(SECRETS["PRIVATE_KEY_FILE_PATH"])
private_key = private_pem_file.read()
private_pem_file.close()
public_pem_file = open(SECRETS["PUBLIC_KEY_FILE_PATH"])
public_key = public_pem_file.read()
public_pem_file.close()
header = {"alg": "RS256"}
lifespan: timedelta = timedelta(
days=0,
seconds=0,
microseconds=0,
milliseconds=0,
minutes=15,
hours=0,
weeks=0,
)
current_time: datetime = datetime.now(
tz=timezone(timedelta(hours=5, minutes=30))
)
current_time_plus_lifespan: datetime = current_time + lifespan
issued_at: datetime = current_time.timestamp()
expiration_time: datetime = current_time_plus_lifespan.timestamp()
not_before: datetime = current_time.timestamp()
jwt_id: UUID = uuid4()
payload: dict[str, Union[float, List[str], UUID, str]] = {
"iss": "HarshitDoshi",
"sub": "Harshit.Doshi@example.org",
"aud": ["https://example.org", "https://authentication.example.org"],
"iat": issued_at,
"exp": expiration_time,
"nbf": not_before,
"jti": jwt_id,
}
jwt_encoded = jwt.encode(header, payload, private_key, check=True).decode("utf-8")
jwt_decoded = jwt.decode(
s=encoded_jwt,
key=public_key,
claims_options={
"iss": {
"essential": True,
"value": "HarshitDoshi",
},
"sub": {
"essential": True,
"value": "Harshit.Doshi@example.org",
},
"aud": {
"essential": True,
"values": ['https://example.org', 'https://authentication.example.org'],
},
"iat": {
"essential": True,
"validate": JWTClaims.validate_iat,
},
"exp": {
"essential": True,
"validate": JWTClaims.validate_exp,
},
"nbf": {
"essential": True,
"validate": JWTClaims.validate_nbf,
},
"jti": {
"essential": True,
"value": "61276430-0784-5e84-b12d-e8b996f6172a",
},
},
)
jwt_decoded.validate()
```
I am doing some basic `print`-based debugging as follows:
```python
for k in jwt_decoded.keys():
print(f"\n{k}: {jwt_decoded[k]} - {type(jwt_decoded[k])}")
```
The resulting output for the above print statements is as follows:
```
iss: HarshitDoshi -
sub: Harshit.Doshi@example.org -
aud: ['https://example.org', 'https://authentication.example.org'] -
iat: 1681022938.106472 -
exp: 1681023838.106472 -
nbf: 1681022938.106472 -
```
Discussed in https://github.com/lepture/authlib/discussions/538