Skip to content

Commit 7ef1339

Browse files
Merge pull request #321 from supertokens/test-cicd/fix-failing-tests
test: Fix failing unit tests for access token v3
2 parents 4dd108b + 9550205 commit 7ef1339

File tree

26 files changed

+659
-195
lines changed

26 files changed

+659
-195
lines changed

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ set-up-hooks:
1717
chmod +x .git/hooks/pre-commit
1818

1919
test:
20-
pytest --reruns 3 --reruns-delay 5 ./tests/
20+
pytest -vv --reruns 3 --reruns-delay 5 ./tests/
2121

2222
dev-install:
2323
pip install -r dev-requirements.txt

supertokens_python/framework/django/django_request.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,10 @@
2121

2222
if TYPE_CHECKING:
2323
from supertokens_python.recipe.session.interfaces import SessionContainer
24+
from django.http import HttpRequest
2425

2526

2627
class DjangoRequest(BaseRequest):
27-
from django.http import HttpRequest
28-
2928
def __init__(self, request: HttpRequest):
3029
super().__init__()
3130
self.request = request

supertokens_python/framework/fastapi/fastapi_request.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,10 @@
2020

2121
if TYPE_CHECKING:
2222
from supertokens_python.recipe.session.interfaces import SessionContainer
23+
from fastapi import Request
2324

2425

2526
class FastApiRequest(BaseRequest):
26-
27-
from fastapi import Request
28-
2927
def __init__(self, request: Request):
3028
super().__init__()
3129
self.request = request

supertokens_python/framework/flask/flask_request.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,10 @@
1919

2020
if TYPE_CHECKING:
2121
from supertokens_python.recipe.session.interfaces import SessionContainer
22+
from flask.wrappers import Request
2223

2324

2425
class FlaskRequest(BaseRequest):
25-
from flask.wrappers import Request
26-
2726
def __init__(self, req: Request):
2827
super().__init__()
2928
self.request = req

supertokens_python/recipe/session/asyncio/__init__.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,13 @@ async def create_new_session_without_request_response(
9393
claims_added_by_other_recipes = (
9494
SessionRecipe.get_instance().get_claims_added_by_other_recipes()
9595
)
96-
final_access_token_payload = access_token_payload
96+
app_info = SessionRecipe.get_instance().app_info
97+
issuer = (
98+
app_info.api_domain.get_as_string_dangerous()
99+
+ app_info.api_base_path.get_as_string_dangerous()
100+
)
101+
102+
final_access_token_payload = {**access_token_payload, "iss": issuer}
97103

98104
for claim in claims_added_by_other_recipes:
99105
update = await claim.build(user_id, user_context)

supertokens_python/recipe/session/exceptions.py

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ def raise_token_theft_exception(user_id: str, session_handle: str) -> NoReturn:
2828
def raise_try_refresh_token_exception(ex: Union[str, Exception]) -> NoReturn:
2929
if isinstance(ex, SuperTokensError):
3030
raise ex
31+
3132
raise TryRefreshTokenError(ex) from None
3233

3334

@@ -36,16 +37,18 @@ def raise_unauthorised_exception(
3637
clear_tokens: bool = True,
3738
response_mutators: Optional[List[ResponseMutator]] = None,
3839
) -> NoReturn:
39-
if response_mutators is None:
40-
response_mutators = []
41-
4240
err = UnauthorisedError(msg, clear_tokens)
43-
err.response_mutators.extend(UnauthorisedError.response_mutators)
41+
42+
if response_mutators is not None:
43+
err.response_mutators.extend(response_mutators)
44+
4445
raise err
4546

4647

4748
class SuperTokensSessionError(SuperTokensError):
48-
response_mutators: List[ResponseMutator] = []
49+
def __init__(self, *args: Any, **kwargs: Any) -> None:
50+
super().__init__(*args, **kwargs)
51+
self.response_mutators: List[ResponseMutator] = []
4952

5053

5154
class TokenTheftError(SuperTokensSessionError):

supertokens_python/recipe/session/jwks.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,8 @@ def reload(self):
4646
with urllib.request.urlopen(self.uri, timeout=self.timeout_sec) as response:
4747
self.jwk_set = PyJWKSet.from_dict(json.load(response)) # type: ignore
4848
self.last_fetch_time = get_timestamp_ms()
49-
except URLError as e:
50-
raise JWKSRequestError(f'Failed to fetch data from the url, err: "{e}"')
49+
except URLError:
50+
raise JWKSRequestError("Failed to fetch jwk set from the configured uri")
5151

5252
def is_cooling_down(self) -> bool:
5353
return (self.last_fetch_time > 0) and (
@@ -81,15 +81,17 @@ def get_matching_key_from_jwt(self, token: str) -> PyJWK:
8181

8282
try:
8383
return self.jwk_set[kid] # type: ignore
84-
except IndexError:
84+
except KeyError:
8585
if not self.is_cooling_down():
8686
# One more attempt to fetch the latest keys
8787
# and then try to find the key again.
8888
self.reload()
8989
try:
9090
return self.jwk_set[kid] # type: ignore
91-
except IndexError:
91+
except KeyError:
9292
pass
93+
except Exception:
94+
raise JWKSKeyNotFoundError("No key found for the given kid")
9395

9496
raise JWKSKeyNotFoundError("No key found for the given kid")
9597

supertokens_python/recipe/session/recipe.py

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -96,13 +96,6 @@ def __init__(
9696
expose_access_token_to_frontend_in_cookie_based_auth: Union[bool, None] = None,
9797
):
9898
super().__init__(recipe_id, app_info)
99-
self.openid_recipe = OpenIdRecipe(
100-
recipe_id,
101-
app_info,
102-
None,
103-
None,
104-
override.openid_feature if override is not None else None,
105-
)
10699
self.config = validate_and_normalise_user_input(
107100
app_info,
108101
cookie_domain,
@@ -117,6 +110,13 @@ def __init__(
117110
use_dynamic_access_token_signing_key,
118111
expose_access_token_to_frontend_in_cookie_based_auth,
119112
)
113+
self.openid_recipe = OpenIdRecipe(
114+
recipe_id,
115+
app_info,
116+
None,
117+
None,
118+
override.openid_feature if override is not None else None,
119+
)
120120
log_debug_message("session init: anti_csrf: %s", self.config.anti_csrf)
121121
if self.config.cookie_domain is not None:
122122
log_debug_message(
@@ -221,7 +221,10 @@ async def handle_api_request(
221221
async def handle_error(
222222
self, request: BaseRequest, err: SuperTokensError, response: BaseResponse
223223
) -> BaseResponse:
224-
if isinstance(err, SuperTokensSessionError):
224+
if (
225+
isinstance(err, SuperTokensSessionError)
226+
and err.response_mutators is not None
227+
):
225228
for mutator in err.response_mutators:
226229
mutator(response)
227230

supertokens_python/recipe/session/session_class.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,9 @@ async def attach_to_request_response(
7070
anti_csrf_response_mutator(self.anti_csrf_token)
7171
)
7272

73-
request.set_session(self)
73+
request.set_session(
74+
self
75+
) # Although this is called in recipe/session/framework/**/__init__.py. It's required in case of python because functions like create_new_session(req, "user-id") can be called in the framework view handler as well
7476

7577
async def revoke_session(self, user_context: Union[Any, None] = None) -> None:
7678
if user_context is None:

supertokens_python/recipe/session/session_functions.py

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,7 @@ async def get_session(
194194
if time_created <= time.time() - JWKCacheMaxAgeInMs:
195195
raise e
196196
else:
197-
# Since v3 (and above) tokens contain a kid we can trust the cache-refresh mechanism of the pyjwt library
197+
# Since v3 (and above) tokens contain a kid we can trust the cache refresh mechanism built on top of the pyjwt lib
198198
# This means we do not need to call the core since the signature wouldn't pass verification anyway.
199199
raise e
200200

@@ -281,13 +281,26 @@ async def get_session(
281281
response["session"]["handle"],
282282
response["session"]["userId"],
283283
response["session"]["userDataInJWT"],
284-
response["accessToken"]["expiry"],
284+
(
285+
response.get("accessToken", {}).get(
286+
"expiry"
287+
) # if we got a new accesstoken we take the expiry time from there
288+
or (
289+
access_token_info is not None
290+
and access_token_info.get("expiryTime")
291+
) # if we didn't get a new access token but could validate the token take that info (alwaysCheckCore === true, or parentRefreshTokenHash1 !== null)
292+
or parsed_access_token.payload[
293+
"expiryTime"
294+
] # if the token didn't pass validation, but we got here, it means it was a v2 token that we didn't have the key cached for.
295+
), # This will throw error if others are none and 'expiryTime' key doesn't exist in the payload
285296
),
286297
GetSessionAPIResponseAccessToken(
287298
response["accessToken"]["token"],
288299
response["accessToken"]["expiry"],
289300
response["accessToken"]["createdTime"],
290-
),
301+
)
302+
if "accessToken" in response
303+
else None,
291304
)
292305
if response["status"] == "UNAUTHORISED":
293306
log_debug_message("getSession: Returning UNAUTHORISED because of core response")

supertokens_python/recipe/session/session_request_functions.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -229,7 +229,13 @@ async def create_new_session_in_request(
229229
user_context = set_request_in_user_context_if_not_defined(user_context, request)
230230

231231
claims_added_by_other_recipes = recipe_instance.get_claims_added_by_other_recipes()
232-
final_access_token_payload = access_token_payload
232+
app_info = recipe_instance.app_info
233+
issuer = (
234+
app_info.api_domain.get_as_string_dangerous()
235+
+ app_info.api_base_path.get_as_string_dangerous()
236+
)
237+
238+
final_access_token_payload = {**access_token_payload, "iss": issuer}
233239

234240
for claim in claims_added_by_other_recipes:
235241
update = await claim.build(user_id, user_context)
@@ -264,7 +270,7 @@ async def create_new_session_in_request(
264270
# We can allow insecure cookie when both website & API domain are localhost or an IP
265271
# When either of them is a different domain, API domain needs to have https and a secure cookie to work
266272
raise Exception(
267-
"Since your API and website domain are different, for sessions to work, please use https on your apiDomain and dont set cookieSecure to false."
273+
"Since your API and website domain are different, for sessions to work, please use https on your apiDomain and don't set cookieSecure to false."
268274
)
269275

270276
disable_anti_csrf = output_transfer_method == "header"

supertokens_python/utils.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -99,9 +99,8 @@ def find_max_version(versions_1: List[str], versions_2: List[str]) -> Union[str,
9999
return max_v
100100

101101

102-
def is_version_gte(version: str, minimum_minor_version: str) -> bool:
103-
assert len(minimum_minor_version.split(".")) == 2
104-
return _get_max_version(version, minimum_minor_version) == version
102+
def is_version_gte(version: str, minimum_version: str) -> bool:
103+
return _get_max_version(version, minimum_version) == version
105104

106105

107106
def _get_max_version(v1: str, v2: str) -> str:

tests/Fastapi/test_fastapi.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -620,9 +620,6 @@ async def refresh_post(
620620
assert res.status_code == 401
621621
assert_info_clears_tokens(info, token_transfer_method)
622622

623-
assert info["sIdRefreshToken"]["value"] == ""
624-
assert info["sIdRefreshToken"]["expires"] == "Thu, 01 Jan 1970 00:00:00 GMT"
625-
626623

627624
@mark.asyncio
628625
@mark.parametrize("token_transfer_method", ["cookie", "header"])

tests/frontendIntegration/django2x/polls/urls.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
path("", views.get_info, name="/"), # type: ignore
1717
path("update-jwt", views.update_jwt, name="update_jwt"), # type: ignore
1818
path("update-jwt-with-handle", views.update_jwt_with_handle, name="update_jwt_with_handle"), # type: ignore
19+
path("session-claims-error", views.session_claim_error_api, name="session_claim_error_api"), # type: ignore
20+
path("403-without-body", views.without_body_403, name="without_body_403"), # type: ignore
1921
path("testing", views.testing, name="testing"), # type: ignore
2022
path("logout", views.logout, name="logout"), # type: ignore
2123
path("revokeAll", views.revoke_all, name="revokeAll"), # type: ignore

0 commit comments

Comments
 (0)