Skip to content

Commit 3627351

Browse files
Merge pull request #317 from supertokens/fix/session-jwt-changes-improvements
fix: Improvements in session jwt changes
2 parents 9e013e9 + c23604e commit 3627351

25 files changed

+548
-505
lines changed

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@
9999
],
100100
keywords="",
101101
install_requires=[
102-
"PyJWT>=2.0.0 ,<3.0.0",
102+
"PyJWT>=2.6.0 ,<3.0.0",
103103
"httpx>=0.15.0 ,<0.24.0",
104104
"pycryptodome==3.10.*",
105105
"tldextract==3.1.0",

supertokens_python/recipe/jwt/recipe.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,10 @@ async def handle_api_request(
9393
self.recipe_implementation,
9494
)
9595

96-
return await jwks_get(self.api_implementation, options)
96+
if request_id == GET_JWKS_API:
97+
return await jwks_get(self.api_implementation, options)
98+
99+
return None
97100

98101
async def handle_error(
99102
self, request: BaseRequest, err: SuperTokensError, response: BaseResponse

supertokens_python/recipe/session/access_token.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ def get_info_from_access_token(
5050
do_anti_csrf_check: bool,
5151
):
5252
# TODO: Add different tests to verify this works as expected
53-
try:
53+
try: # pylint: disable=too-many-nested-blocks
5454
payload: Optional[Dict[str, Any]] = None
5555

5656
if jwt_info.version < 3:
@@ -82,7 +82,7 @@ def get_info_from_access_token(
8282
)
8383
payload = jwt.decode( # type: ignore
8484
jwt_info.raw_token_string,
85-
matching_key,
85+
matching_key.key, # type: ignore
8686
algorithms=["RS256"],
8787
options={"verify_signature": True, "verify_exp": True},
8888
)

supertokens_python/recipe/session/asyncio/__init__.py

Lines changed: 39 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -11,49 +11,37 @@
1111
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
1212
# License for the specific language governing permissions and limitations
1313
# under the License.
14-
from typing import Any, Dict, List, Union, TypeVar, Callable, Optional
14+
from typing import Any, Callable, Dict, List, Optional, TypeVar, Union
1515

16-
from supertokens_python.exceptions import SuperTokensError
1716
from supertokens_python.recipe.openid.interfaces import (
1817
GetOpenIdDiscoveryConfigurationResult,
1918
)
2019
from supertokens_python.recipe.session.interfaces import (
20+
ClaimsValidationResult,
21+
GetClaimValueOkResult,
22+
JSONObject,
2123
RegenerateAccessTokenOkResult,
22-
SessionContainer,
23-
SessionInformationResult,
2424
SessionClaim,
2525
SessionClaimValidator,
26+
SessionContainer,
2627
SessionDoesNotExistError,
27-
ClaimsValidationResult,
28-
JSONObject,
29-
GetClaimValueOkResult,
30-
GetSessionUnauthorizedErrorResult,
31-
GetSessionTryRefreshTokenErrorResult,
32-
GetSessionClaimValidationErrorResult,
33-
GetSessionClaimValidationErrorResponseObject,
34-
CreateNewSessionResult,
35-
GetSessionOkResult,
36-
RefreshSessionOkResult,
37-
RefreshSessionUnauthorizedResult,
38-
RefreshSessionTokenTheftErrorResult,
39-
)
40-
from supertokens_python.recipe.session.recipe import (
41-
SessionRecipe,
42-
)
43-
from ..session_request_functions import (
44-
get_session_from_request,
45-
create_new_session_in_request,
46-
refresh_session_in_request,
28+
SessionInformationResult,
4729
)
30+
from supertokens_python.recipe.session.recipe import SessionRecipe
4831
from supertokens_python.types import MaybeAwaitable
4932
from supertokens_python.utils import FRAMEWORKS, resolve
50-
from ..exceptions import InvalidClaimsError
51-
from ..utils import get_required_claim_validators
33+
5234
from ...jwt.interfaces import (
5335
CreateJwtOkResult,
5436
CreateJwtResultUnsupportedAlgorithm,
5537
GetJWKSResult,
5638
)
39+
from ..session_request_functions import (
40+
create_new_session_in_request,
41+
get_session_from_request,
42+
refresh_session_in_request,
43+
)
44+
from ..utils import get_required_claim_validators
5745

5846
_T = TypeVar("_T")
5947

@@ -94,7 +82,7 @@ async def create_new_session_without_request_response(
9482
session_data_in_database: Union[Dict[str, Any], None] = None,
9583
disable_anti_csrf: bool = False,
9684
user_context: Union[None, Dict[str, Any]] = None,
97-
) -> CreateNewSessionResult:
85+
) -> SessionContainer:
9886
if user_context is None:
9987
user_context = {}
10088
if session_data_in_database is None:
@@ -294,6 +282,9 @@ async def get_session(
294282
if user_context is None:
295283
user_context = {}
296284

285+
if session_required is None:
286+
session_required = True
287+
297288
recipe_instance = SessionRecipe.get_instance()
298289
recipe_interface_impl = recipe_instance.recipe_implementation
299290
config = recipe_instance.config
@@ -314,6 +305,7 @@ async def get_session_without_request_response(
314305
access_token: str,
315306
anti_csrf_token: Optional[str] = None,
316307
anti_csrf_check: Optional[bool] = None,
308+
session_required: Optional[bool] = None,
317309
check_database: Optional[bool] = None,
318310
override_global_claim_validators: Optional[
319311
Callable[
@@ -322,12 +314,7 @@ async def get_session_without_request_response(
322314
]
323315
] = None,
324316
user_context: Union[None, Dict[str, Any]] = None,
325-
) -> Union[
326-
GetSessionOkResult,
327-
GetSessionUnauthorizedErrorResult,
328-
GetSessionTryRefreshTokenErrorResult,
329-
GetSessionClaimValidationErrorResult,
330-
]:
317+
) -> Optional[SessionContainer]:
331318
"""Tries to validate an access token and build a Session object from it.
332319
333320
Notes about anti-csrf checking:
@@ -338,49 +325,44 @@ async def get_session_without_request_response(
338325
Args:
339326
- access_token: The access token extracted from the authorization header or cookies
340327
- anti_csrf_token: The anti-csrf token extracted from the authorization header or cookies. Can be undefined if antiCsrfCheck is false
341-
- anti_csrf_check: If true, anti-csrf checking will be done. If false, it will be skipped. Defaults behaviour to check.
342-
- check_database: If true, the session will be checked in the database. If false, it will be skipped. Defaults behaviour to skip.
328+
- anti_csrf_check: If true, anti-csrf checking will be done. If false, it will be skipped. Default behaviour is to check.
329+
- session_required: If true, throws an error if the session does not exist. Default is True.
330+
- check_database: If true, the session will be checked in the database. If false, it will be skipped. Default behaviour is to skip.
343331
- override_global_claim_validators: Alter the
344332
- user_context: user context
345333
346-
Returned values:
347-
- GetSessionOkResult: The session was successfully validated, including claim validation
348-
- GetSessionClaimValidationErrorResult: While the access token is valid, one or more claim validators have failed. Our frontend SDKs expect a 403 response the contents matching the value returned from this function.
349-
- GetSessionTryRefreshTokenErrorResult: This means, that the access token structure was valid, but it didn't pass validation for some reason and the user should call the refresh API.
350-
- You can send a 401 response to trigger this behaviour if you are using our frontend SDKs
351-
- GetSessionUnauthorizedErrorResult: This means that the access token likely doesn't belong to a SuperTokens session. If this is unexpected, it's best handled by sending a 401 response.
334+
Results:
335+
- OK: The session was successfully validated, including claim validation
336+
- CLAIM_VALIDATION_ERROR: While the access token is valid, one or more claim validators have failed. Our frontend SDKs expect a 403 response the contents matching the value returned from this function.
337+
- TRY_REFRESH_TOKEN_ERROR: This means, that the access token structure was valid, but it didn't pass validation for some reason and the user should call the refresh API.
338+
You can send a 401 response to trigger this behaviour if you are using our frontend SDKs
339+
- UNAUTHORISED: This means that the access token likely doesn't belong to a SuperTokens session. If this is unexpected, it's best handled by sending a 401 response.
352340
"""
353341
if user_context is None:
354342
user_context = {}
355343

344+
if session_required is None:
345+
session_required = True
346+
356347
recipe_interface_impl = SessionRecipe.get_instance().recipe_implementation
357348

358-
res = await recipe_interface_impl.get_session(
349+
session = await recipe_interface_impl.get_session(
359350
access_token,
360351
anti_csrf_token,
361352
anti_csrf_check,
353+
session_required,
362354
check_database,
363355
override_global_claim_validators,
364356
user_context,
365357
)
366358

367-
if isinstance(res, GetSessionOkResult):
359+
if session is not None:
368360
claim_validators = await get_required_claim_validators(
369-
res.session, override_global_claim_validators, user_context
361+
session, override_global_claim_validators, user_context
370362
)
371-
try:
372-
await res.session.assert_claims(claim_validators, user_context)
373-
except SuperTokensError as e:
374-
if isinstance(e, InvalidClaimsError):
375-
return GetSessionClaimValidationErrorResult(
376-
error=e,
377-
response=GetSessionClaimValidationErrorResponseObject(
378-
message="invalid claim", claim_validation_errors=e.payload
379-
),
380-
)
381-
raise e
363+
await session.assert_claims(claim_validators, user_context)
382364

383-
return res
365+
return session
384366

385367

386368
async def refresh_session(
@@ -412,11 +394,7 @@ async def refresh_session_without_request_response(
412394
disable_anti_csrf: bool = False,
413395
anti_csrf_token: Optional[str] = None,
414396
user_context: Optional[Dict[str, Any]] = None,
415-
) -> Union[
416-
RefreshSessionOkResult,
417-
RefreshSessionUnauthorizedResult,
418-
RefreshSessionTokenTheftErrorResult,
419-
]:
397+
) -> SessionContainer:
420398
if user_context is None:
421399
user_context = {}
422400

supertokens_python/recipe/session/constants.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,8 @@
3333

3434
available_token_transfer_methods: List[TokenTransferMethod] = ["cookie", "header"]
3535

36-
JWKCacheMaxAgeInMs = 60 * 1000
37-
JWKRequestCooldownInMs = 500 * 1000
36+
JWKCacheMaxAgeInMs = 60 * 1000 # 1min
37+
JWKRequestCooldownInMs = 500 # 0.5s
3838
protected_props = [
3939
"sub",
4040
"iat",

supertokens_python/recipe/session/cookie_and_header.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -262,7 +262,7 @@ def _set_token(
262262
expires: int,
263263
transfer_method: TokenTransferMethod,
264264
):
265-
log_debug_message(f"Setting {token_type} token as {transfer_method}")
265+
log_debug_message("Setting %s token as %s", token_type, transfer_method)
266266
if transfer_method == "cookie":
267267
_set_cookie(
268268
response,

supertokens_python/recipe/session/exceptions.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,17 @@ def raise_try_refresh_token_exception(ex: Union[str, Exception]) -> NoReturn:
3131
raise TryRefreshTokenError(ex) from None
3232

3333

34-
def raise_unauthorised_exception(msg: str, clear_tokens: bool = True) -> NoReturn:
35-
raise UnauthorisedError(msg, clear_tokens) from None
34+
def raise_unauthorised_exception(
35+
msg: str,
36+
clear_tokens: bool = True,
37+
response_mutators: Optional[List[ResponseMutator]] = None,
38+
) -> NoReturn:
39+
if response_mutators is None:
40+
response_mutators = []
41+
42+
err = UnauthorisedError(msg, clear_tokens)
43+
err.response_mutators.extend(UnauthorisedError.response_mutators)
44+
raise err
3645

3746

3847
class SuperTokensSessionError(SuperTokensError):

0 commit comments

Comments
 (0)