Skip to content

Commit 6fc22f4

Browse files
Th3R3p0gjtorikian
authored andcommitted
adding jwt_leeway param to allow configuring the leeway param on PyJWT jwt.decode method
1 parent b34f46c commit 6fc22f4

File tree

5 files changed

+38
-1
lines changed

5 files changed

+38
-1
lines changed

src/workos/_base_client.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ class BaseClient(ClientConfiguration):
2727
_base_url: str
2828
_client_id: str
2929
_request_timeout: int
30+
_jwt_leeway: float
3031

3132
def __init__(
3233
self,
@@ -35,6 +36,7 @@ def __init__(
3536
client_id: Optional[str],
3637
base_url: Optional[str] = None,
3738
request_timeout: Optional[int] = None,
39+
jwt_leeway: float = 0,
3840
) -> None:
3941
api_key = api_key or os.getenv("WORKOS_API_KEY")
4042
if api_key is None:
@@ -66,6 +68,8 @@ def __init__(
6668
else int(os.getenv("WORKOS_REQUEST_TIMEOUT", DEFAULT_REQUEST_TIMEOUT))
6769
)
6870

71+
self._jwt_leeway = jwt_leeway
72+
6973
@property
7074
@abstractmethod
7175
def api_keys(self) -> ApiKeysModule: ...
@@ -136,3 +140,7 @@ def client_id(self) -> str:
136140
@property
137141
def request_timeout(self) -> int:
138142
return self._request_timeout
143+
144+
@property
145+
def jwt_leeway(self) -> float:
146+
return self._jwt_leeway

src/workos/async_client.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,12 +32,14 @@ def __init__(
3232
client_id: Optional[str] = None,
3333
base_url: Optional[str] = None,
3434
request_timeout: Optional[int] = None,
35+
jwt_leeway: float = 0,
3536
):
3637
super().__init__(
3738
api_key=api_key,
3839
client_id=client_id,
3940
base_url=base_url,
4041
request_timeout=request_timeout,
42+
jwt_leeway=jwt_leeway,
4143
)
4244
self._http_client = AsyncHTTPClient(
4345
api_key=self._api_key,

src/workos/client.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,12 +32,14 @@ def __init__(
3232
client_id: Optional[str] = None,
3333
base_url: Optional[str] = None,
3434
request_timeout: Optional[int] = None,
35+
jwt_leeway: float = 0,
3536
):
3637
super().__init__(
3738
api_key=api_key,
3839
client_id=client_id,
3940
base_url=base_url,
4041
request_timeout=request_timeout,
42+
jwt_leeway=jwt_leeway,
4143
)
4244
self._http_client = SyncHTTPClient(
4345
api_key=self._api_key,

src/workos/session.py

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ class SessionModule(Protocol):
3535
cookie_password: str
3636
jwks: PyJWKClient
3737
jwk_algorithms: List[str]
38+
jwt_leeway: float
3839

3940
def __init__(
4041
self,
@@ -43,6 +44,7 @@ def __init__(
4344
client_id: str,
4445
session_data: str,
4546
cookie_password: str,
47+
jwt_leeway: float = 0,
4648
) -> None:
4749
# If the cookie password is not provided, throw an error
4850
if cookie_password is None or cookie_password == "":
@@ -52,6 +54,7 @@ def __init__(
5254
self.client_id = client_id
5355
self.session_data = session_data
5456
self.cookie_password = cookie_password
57+
self.jwt_leeway = jwt_leeway
5558

5659
self.jwks = _get_jwks_client(self.user_management.get_jwks_url())
5760

@@ -91,13 +94,13 @@ def authenticate(
9194
signing_key.key,
9295
algorithms=self.jwk_algorithms,
9396
options={"verify_aud": False},
97+
leeway=self.jwt_leeway,
9498
)
9599
except jwt.exceptions.InvalidTokenError:
96100
return AuthenticateWithSessionCookieErrorResponse(
97101
authenticated=False,
98102
reason=AuthenticateWithSessionCookieFailureReason.INVALID_JWT,
99103
)
100-
101104
return AuthenticateWithSessionCookieSuccessResponse(
102105
authenticated=True,
103106
session_id=decoded["sid"],
@@ -137,6 +140,20 @@ def get_logout_url(self, return_to: Optional[str] = None) -> str:
137140
)
138141
return str(result)
139142

143+
def _is_valid_jwt(self, token: str) -> bool:
144+
try:
145+
signing_key = self.jwks.get_signing_key_from_jwt(token)
146+
jwt.decode(
147+
token,
148+
signing_key.key,
149+
algorithms=self.jwk_algorithms,
150+
options={"verify_aud": False},
151+
leeway=self.jwt_leeway,
152+
)
153+
return True
154+
except jwt.exceptions.InvalidTokenError:
155+
return False
156+
140157
@staticmethod
141158
def seal_data(data: Dict[str, Any], key: str) -> str:
142159
fernet = Fernet(key)
@@ -163,6 +180,7 @@ def __init__(
163180
client_id: str,
164181
session_data: str,
165182
cookie_password: str,
183+
jwt_leeway: float = 0,
166184
) -> None:
167185
# If the cookie password is not provided, throw an error
168186
if cookie_password is None or cookie_password == "":
@@ -172,6 +190,7 @@ def __init__(
172190
self.client_id = client_id
173191
self.session_data = session_data
174192
self.cookie_password = cookie_password
193+
self.jwt_leeway = jwt_leeway
175194

176195
self.jwks = _get_jwks_client(self.user_management.get_jwks_url())
177196

@@ -224,6 +243,7 @@ def refresh(
224243
signing_key.key,
225244
algorithms=self.jwk_algorithms,
226245
options={"verify_aud": False},
246+
leeway=self.jwt_leeway,
227247
)
228248

229249
return RefreshWithSessionCookieSuccessResponse(
@@ -255,6 +275,7 @@ def __init__(
255275
client_id: str,
256276
session_data: str,
257277
cookie_password: str,
278+
jwt_leeway: float = 0,
258279
) -> None:
259280
# If the cookie password is not provided, throw an error
260281
if cookie_password is None or cookie_password == "":
@@ -264,6 +285,7 @@ def __init__(
264285
self.client_id = client_id
265286
self.session_data = session_data
266287
self.cookie_password = cookie_password
288+
self.jwt_leeway = jwt_leeway
267289

268290
self.jwks = _get_jwks_client(self.user_management.get_jwks_url())
269291

@@ -316,6 +338,7 @@ async def refresh(
316338
signing_key.key,
317339
algorithms=self.jwk_algorithms,
318340
options={"verify_aud": False},
341+
leeway=self.jwt_leeway,
319342
)
320343

321344
return RefreshWithSessionCookieSuccessResponse(

src/workos/user_management.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -956,6 +956,7 @@ def load_sealed_session(
956956
client_id=self._http_client.client_id,
957957
session_data=sealed_session,
958958
cookie_password=cookie_password,
959+
jwt_leeway=self._client_configuration.jwt_leeway,
959960
)
960961

961962
def get_user(self, user_id: str) -> User:
@@ -1679,6 +1680,7 @@ async def load_sealed_session(
16791680
client_id=self._http_client.client_id,
16801681
session_data=sealed_session,
16811682
cookie_password=cookie_password,
1683+
jwt_leeway=self._client_configuration.jwt_leeway,
16821684
)
16831685

16841686
async def get_user(self, user_id: str) -> User:

0 commit comments

Comments
 (0)