lepture / authlib

The ultimate Python library in building OAuth, OpenID Connect clients and servers. JWS,JWE,JWK,JWA,JWT included.
https://authlib.org/
BSD 3-Clause "New" or "Revised" License
4.55k stars 452 forks source link

JWTClaims types seem wrong #539

Closed HarshitDoshi closed 1 year ago

HarshitDoshi commented 1 year ago

Discussed in https://github.com/lepture/authlib/discussions/538

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 - ```