Skip to content

Commit 9fc73bd

Browse files
committed
Enhance JWT validation in SessionService by adding pre-flight checks for algorithm restrictions. Update tests to validate handling of disallowed algorithms, including "none".
1 parent a44b343 commit 9fc73bd

2 files changed

Lines changed: 53 additions & 17 deletions

File tree

src/corbado_python_sdk/services/implementation/session_service.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import jwt
22
from jwt import (
3+
get_unverified_header, # ← added
34
ExpiredSignatureError,
45
ImmatureSignatureError,
56
InvalidAlgorithmError,
@@ -80,6 +81,23 @@ def validate_token(self, session_token: StrictStr) -> UserEntity:
8081
message=ValidationErrorType.CODE_JWT_EMPTY_SESSION_TOKEN.name,
8182
)
8283

84+
# ---- pre-flight alg rejection ----
85+
try:
86+
header = get_unverified_header(session_token)
87+
except Exception as err:
88+
raise TokenValidationException(
89+
error_type=ValidationErrorType.CODE_JWT_GENERAL,
90+
message=f"Error parsing JWT header: {session_token}",
91+
original_exception=err,
92+
)
93+
if header.get("alg") not in ALLOWED_ALGS:
94+
raise TokenValidationException(
95+
error_type=ValidationErrorType.CODE_JWT_INVALID_SIGNATURE,
96+
message="Algorithm not allowed",
97+
original_exception=InvalidAlgorithmError("Algorithm not allowed"),
98+
)
99+
# -----------------------------------------
100+
83101
# retrieve signing key
84102
try:
85103
signing_key: jwt.PyJWK = self._jwk_client.get_signing_key_from_jwt(token=session_token)

tests/unit/test_session_service.py

Lines changed: 35 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
ImmatureSignatureError,
1111
InvalidAlgorithmError,
1212
InvalidSignatureError,
13-
PyJWKClientError,
1413
encode,
1514
)
1615
from pydantic import ValidationError
@@ -129,8 +128,8 @@ def _provide_jwts(self):
129128
False,
130129
"""eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6
131130
IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.dyt0CoTl4WoVjAHI9Q_CwSKhl6d_9rhM3NrXuJttkao""",
132-
PyJWKClientError,
133-
'Unable to find a signing key that matches: "None"',
131+
InvalidAlgorithmError,
132+
"Algorithm not allowed",
134133
),
135134
# Not before (nfb) in future
136135
(
@@ -180,6 +179,13 @@ def _provide_jwts(self):
180179
None,
181180
None,
182181
),
182+
# Disallowed algorithm "none"
183+
(
184+
False,
185+
self._generate_jwt(iss="https://auth.acme.com", exp=int(time()) + 100, nbf=int(time()) - 100, algorithm="none"),
186+
InvalidAlgorithmError,
187+
"Algorithm not allowed",
188+
),
183189
# Success with old Frontend API URL in config (2)
184190
(
185191
True,
@@ -194,20 +200,17 @@ def _provide_jwts(self):
194200
None,
195201
None,
196202
),
197-
# Disallowed algorithm "none"
198-
(
199-
False,
200-
"eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0."
201-
"eyJpc3MiOiJodHRwczovL2F1dGguYWNtZS5jb20iLCJzdWIiOiIxMjM0NSIsImlhdCI6"
202-
+ str(int(time()))
203-
+ "f.",
204-
InvalidAlgorithmError,
205-
"Algorithm not allowed",
206-
),
207203
]
208204

209205
@classmethod
210-
def _generate_jwt(cls, iss: str, exp: int, nbf: int, valid_key: bool = True) -> str:
206+
def _generate_jwt(
207+
cls,
208+
iss: str,
209+
exp: int,
210+
nbf: int,
211+
valid_key: bool = True,
212+
algorithm: str = "RS256",
213+
) -> str:
211214
payload = {
212215
"iss": iss,
213216
"iat": int(time()),
@@ -217,9 +220,24 @@ def _generate_jwt(cls, iss: str, exp: int, nbf: int, valid_key: bool = True) ->
217220
"name": TEST_NAME,
218221
}
219222

220-
if valid_key:
221-
return encode(payload, key=cls.private_key, algorithm="RS256", headers={"kid": "kid123"})
222-
return encode(payload, key=cls.invalid_private_key, algorithm="RS256", headers={"kid": "kid123"})
223+
key_to_use = cls.private_key if valid_key else cls.invalid_private_key
224+
225+
# unsecured JWT (“none”)
226+
if algorithm.lower() == "none":
227+
# key must be None for alg=none
228+
return encode(
229+
payload,
230+
key=None,
231+
headers={"alg": "none", "typ": "JWT"},
232+
)
233+
234+
# signed JWT (RS256 by default)
235+
return encode(
236+
payload,
237+
key=key_to_use,
238+
algorithm=algorithm,
239+
headers={"kid": "kid123"},
240+
)
223241

224242

225243
class TestSessionService(TestBase):

0 commit comments

Comments
 (0)