diff --git a/CHANGELOG.md b/CHANGELOG.md index 4aeb37661..24d5c5466 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,20 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [unreleased] +## [0.16.8] - 2023-11-7 + +### Added + +- Added `network_interceptor` to the `supertokens_config` in `init`. + - This can be used to capture/modify all the HTTP requests sent to the core. + - Solves the issue - https://github.com/supertokens/supertokens-core/issues/865 + +### Fixes +- The sync functions `create_user_id_mapping` and `delete_user_id_mapping` now take the `force` parameter as an optional argument, just like their async counterparts. +- Functions `get_users_oldest_first`, `get_users_newest_first`, `get_user_count`, `delete_user`, `create_user_id_mapping`, `get_user_id_mapping`, `delete_user_id_mapping` and `update_or_delete_user_id_mapping_info` now accept `user_context` as an optional argument. +- Fixed the dependencies in the example apps + - Example apps will now fetch the latest version of the frameworks + ## [0.16.7] - 2023-11-2 - Added `debug` flag in `init()`. If set to `True`, debug logs will be printed. diff --git a/examples/with-django/with-thirdpartyemailpassword/requirements.txt b/examples/with-django/with-thirdpartyemailpassword/requirements.txt index ef897cc9b..b40b15016 100644 --- a/examples/with-django/with-thirdpartyemailpassword/requirements.txt +++ b/examples/with-django/with-thirdpartyemailpassword/requirements.txt @@ -1,4 +1,3 @@ -Django==4.0.4 -django-cors-headers==3.12.0 -python-dotenv==0.19.2 -supertokens-python +django-cors-headers +python-dotenv +supertokens-python \ No newline at end of file diff --git a/examples/with-fastapi/with-thirdpartyemailpassword/requirements.txt b/examples/with-fastapi/with-thirdpartyemailpassword/requirements.txt index d35e05189..f4011e5eb 100644 --- a/examples/with-fastapi/with-thirdpartyemailpassword/requirements.txt +++ b/examples/with-fastapi/with-thirdpartyemailpassword/requirements.txt @@ -1,4 +1,4 @@ -fastapi==0.68.1 -uvicorn==0.16.0 -python-dotenv==0.19.2 +fastapi +uvicorn +python-dotenv supertokens-python \ No newline at end of file diff --git a/examples/with-flask/with-thirdpartyemailpassword/requirements.txt b/examples/with-flask/with-thirdpartyemailpassword/requirements.txt index 04ecb9f91..ecc52b159 100644 --- a/examples/with-flask/with-thirdpartyemailpassword/requirements.txt +++ b/examples/with-flask/with-thirdpartyemailpassword/requirements.txt @@ -1,4 +1,4 @@ -flask==2.0.1 -flask_cors==3.0.10 -python-dotenv==0.19.2 +flask +flask_cors +python-dotenv supertokens-python \ No newline at end of file diff --git a/setup.py b/setup.py index 96d70492a..da6cc39e2 100644 --- a/setup.py +++ b/setup.py @@ -70,7 +70,7 @@ setup( name="supertokens_python", - version="0.16.7", + version="0.16.8", author="SuperTokens", license="Apache 2.0", author_email="team@supertokens.com", diff --git a/supertokens_python/asyncio/__init__.py b/supertokens_python/asyncio/__init__.py index ac0a4805d..59796af43 100644 --- a/supertokens_python/asyncio/__init__.py +++ b/supertokens_python/asyncio/__init__.py @@ -11,7 +11,7 @@ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. -from typing import Dict, List, Optional, Union +from typing import Dict, List, Optional, Union, Any from supertokens_python import Supertokens from supertokens_python.interfaces import ( @@ -33,9 +33,16 @@ async def get_users_oldest_first( pagination_token: Union[str, None] = None, include_recipe_ids: Union[None, List[str]] = None, query: Union[None, Dict[str, str]] = None, + user_context: Optional[Dict[str, Any]] = None, ) -> UsersResponse: return await Supertokens.get_instance().get_users( - tenant_id, "ASC", limit, pagination_token, include_recipe_ids, query + tenant_id, + "ASC", + limit, + pagination_token, + include_recipe_ids, + query, + user_context, ) @@ -45,22 +52,33 @@ async def get_users_newest_first( pagination_token: Union[str, None] = None, include_recipe_ids: Union[None, List[str]] = None, query: Union[None, Dict[str, str]] = None, + user_context: Optional[Dict[str, Any]] = None, ) -> UsersResponse: return await Supertokens.get_instance().get_users( - tenant_id, "DESC", limit, pagination_token, include_recipe_ids, query + tenant_id, + "DESC", + limit, + pagination_token, + include_recipe_ids, + query, + user_context, ) async def get_user_count( - include_recipe_ids: Union[None, List[str]] = None, tenant_id: Optional[str] = None + include_recipe_ids: Union[None, List[str]] = None, + tenant_id: Optional[str] = None, + user_context: Optional[Dict[str, Any]] = None, ) -> int: return await Supertokens.get_instance().get_user_count( - include_recipe_ids, tenant_id + include_recipe_ids, tenant_id, user_context ) -async def delete_user(user_id: str) -> None: - return await Supertokens.get_instance().delete_user(user_id) +async def delete_user( + user_id: str, user_context: Optional[Dict[str, Any]] = None +) -> None: + return await Supertokens.get_instance().delete_user(user_id, user_context) async def create_user_id_mapping( @@ -68,30 +86,39 @@ async def create_user_id_mapping( external_user_id: str, external_user_id_info: Optional[str] = None, force: Optional[bool] = None, + user_context: Optional[Dict[str, Any]] = None, ) -> Union[ CreateUserIdMappingOkResult, UnknownSupertokensUserIDError, UserIdMappingAlreadyExistsError, ]: return await Supertokens.get_instance().create_user_id_mapping( - supertokens_user_id, external_user_id, external_user_id_info, force + supertokens_user_id, + external_user_id, + external_user_id_info, + force, + user_context, ) async def get_user_id_mapping( user_id: str, user_id_type: Optional[UserIDTypes] = None, + user_context: Optional[Dict[str, Any]] = None, ) -> Union[GetUserIdMappingOkResult, UnknownMappingError]: - return await Supertokens.get_instance().get_user_id_mapping(user_id, user_id_type) + return await Supertokens.get_instance().get_user_id_mapping( + user_id, user_id_type, user_context + ) async def delete_user_id_mapping( user_id: str, user_id_type: Optional[UserIDTypes] = None, force: Optional[bool] = None, + user_context: Optional[Dict[str, Any]] = None, ) -> DeleteUserIdMappingOkResult: return await Supertokens.get_instance().delete_user_id_mapping( - user_id, user_id_type, force + user_id, user_id_type, force, user_context ) @@ -99,7 +126,8 @@ async def update_or_delete_user_id_mapping_info( user_id: str, user_id_type: Optional[UserIDTypes] = None, external_user_id_info: Optional[str] = None, + user_context: Optional[Dict[str, Any]] = None, ) -> Union[UpdateOrDeleteUserIdMappingInfoOkResult, UnknownMappingError]: return await Supertokens.get_instance().update_or_delete_user_id_mapping_info( - user_id, user_id_type, external_user_id_info + user_id, user_id_type, external_user_id_info, user_context ) diff --git a/supertokens_python/constants.py b/supertokens_python/constants.py index 4b79a48d9..9c45916b8 100644 --- a/supertokens_python/constants.py +++ b/supertokens_python/constants.py @@ -14,7 +14,7 @@ from __future__ import annotations SUPPORTED_CDI_VERSIONS = ["3.0"] -VERSION = "0.16.7" +VERSION = "0.16.8" TELEMETRY = "/telemetry" USER_COUNT = "/users/count" USER_DELETE = "/user/remove" diff --git a/supertokens_python/querier.py b/supertokens_python/querier.py index d2dd0749d..69165f2c1 100644 --- a/supertokens_python/querier.py +++ b/supertokens_python/querier.py @@ -14,10 +14,9 @@ from __future__ import annotations import asyncio - from json import JSONDecodeError from os import environ -from typing import TYPE_CHECKING, Any, Awaitable, Callable, Dict, Optional +from typing import TYPE_CHECKING, Any, Awaitable, Callable, Dict, Optional, Tuple from httpx import AsyncClient, ConnectTimeout, NetworkError, Response @@ -50,6 +49,25 @@ class Querier: api_version = None __last_tried_index: int = 0 __hosts_alive_for_testing: Set[str] = set() + network_interceptor: Optional[ + Callable[ + [ + str, + str, + Dict[str, Any], + Optional[Dict[str, Any]], + Optional[Dict[str, Any]], + Optional[Dict[str, Any]], + ], + Tuple[ + str, + str, + Dict[str, Any], + Optional[Dict[str, Any]], + Optional[Dict[str, Any]], + ], + ] + ] = None def __init__(self, hosts: List[Host], rid_to_core: Union[None, str] = None): self.__hosts = hosts @@ -141,7 +159,29 @@ def get_instance(rid_to_core: Union[str, None] = None): return Querier(Querier.__hosts, rid_to_core) @staticmethod - def init(hosts: List[Host], api_key: Union[str, None] = None): + def init( + hosts: List[Host], + api_key: Union[str, None] = None, + network_interceptor: Optional[ + Callable[ + [ + str, + str, + Dict[str, Any], + Optional[Dict[str, Any]], + Optional[Dict[str, Any]], + Optional[Dict[str, Any]], + ], + Tuple[ + str, + str, + Dict[str, Any], + Optional[Dict[str, Any]], + Optional[Dict[str, Any]], + ], + ] + ] = None, + ): if not Querier.__init_called: Querier.__init_called = True Querier.__hosts = hosts @@ -149,6 +189,7 @@ def init(hosts: List[Host], api_key: Union[str, None] = None): Querier.api_version = None Querier.__last_tried_index = 0 Querier.__hosts_alive_for_testing = set() + Querier.network_interceptor = network_interceptor async def __get_headers_with_api_version(self, path: NormalisedURLPath): headers = {API_VERSION_HEADER: await self.get_api_version()} @@ -159,17 +200,33 @@ async def __get_headers_with_api_version(self, path: NormalisedURLPath): return headers async def send_get_request( - self, path: NormalisedURLPath, params: Union[Dict[str, Any], None] = None + self, + path: NormalisedURLPath, + params: Union[Dict[str, Any], None], + user_context: Union[Dict[str, Any], None], ) -> Dict[str, Any]: if params is None: params = {} async def f(url: str, method: str) -> Response: + headers = await self.__get_headers_with_api_version(path) + nonlocal params + if Querier.network_interceptor is not None: + ( + url, + method, + headers, + params, + _, + ) = Querier.network_interceptor( # pylint:disable=not-callable + url, method, headers, params, {}, user_context + ) + return await self.api_request( url, method, 2, - headers=await self.__get_headers_with_api_version(path), + headers=headers, params=params, ) @@ -178,7 +235,8 @@ async def f(url: str, method: str) -> Response: async def send_post_request( self, path: NormalisedURLPath, - data: Union[Dict[str, Any], None] = None, + data: Union[Dict[str, Any], None], + user_context: Union[Dict[str, Any], None], test: bool = False, ) -> Dict[str, Any]: if data is None: @@ -195,35 +253,64 @@ async def send_post_request( headers["content-type"] = "application/json; charset=utf-8" async def f(url: str, method: str) -> Response: + nonlocal headers, data + if Querier.network_interceptor is not None: + ( + url, + method, + headers, + _, + data, + ) = Querier.network_interceptor( # pylint:disable=not-callable + url, method, headers, {}, data, user_context + ) return await self.api_request( url, method, 2, - headers=await self.__get_headers_with_api_version(path), + headers=headers, json=data, ) return await self.__send_request_helper(path, "POST", f, len(self.__hosts)) async def send_delete_request( - self, path: NormalisedURLPath, params: Union[Dict[str, Any], None] = None + self, + path: NormalisedURLPath, + params: Union[Dict[str, Any], None], + user_context: Union[Dict[str, Any], None], ) -> Dict[str, Any]: if params is None: params = {} async def f(url: str, method: str) -> Response: + headers = await self.__get_headers_with_api_version(path) + nonlocal params + if Querier.network_interceptor is not None: + ( + url, + method, + headers, + params, + _, + ) = Querier.network_interceptor( # pylint:disable=not-callable + url, method, headers, params, {}, user_context + ) return await self.api_request( url, method, 2, - headers=await self.__get_headers_with_api_version(path), + headers=headers, params=params, ) return await self.__send_request_helper(path, "DELETE", f, len(self.__hosts)) async def send_put_request( - self, path: NormalisedURLPath, data: Union[Dict[str, Any], None] = None + self, + path: NormalisedURLPath, + data: Union[Dict[str, Any], None], + user_context: Union[Dict[str, Any], None], ) -> Dict[str, Any]: if data is None: data = {} @@ -232,6 +319,17 @@ async def send_put_request( headers["content-type"] = "application/json; charset=utf-8" async def f(url: str, method: str) -> Response: + nonlocal headers, data + if Querier.network_interceptor is not None: + ( + url, + method, + headers, + _, + data, + ) = Querier.network_interceptor( # pylint:disable=not-callable + url, method, headers, {}, data, user_context + ) return await self.api_request(url, method, 2, headers=headers, json=data) return await self.__send_request_helper(path, "PUT", f, len(self.__hosts)) diff --git a/supertokens_python/recipe/dashboard/api/analytics.py b/supertokens_python/recipe/dashboard/api/analytics.py index f22bfb015..d464bdfd5 100644 --- a/supertokens_python/recipe/dashboard/api/analytics.py +++ b/supertokens_python/recipe/dashboard/api/analytics.py @@ -57,7 +57,9 @@ async def handle_analytics_post( try: response = await Querier.get_instance().send_get_request( - NormalisedURLPath("/telemetry") + NormalisedURLPath("/telemetry"), + None, + _user_context, ) if response is not None: if ( diff --git a/supertokens_python/recipe/dashboard/api/search/getTags.py b/supertokens_python/recipe/dashboard/api/search/getTags.py index fd034d88d..a365fb412 100644 --- a/supertokens_python/recipe/dashboard/api/search/getTags.py +++ b/supertokens_python/recipe/dashboard/api/search/getTags.py @@ -27,6 +27,6 @@ async def handle_get_tags( _: APIInterface, _tenant_id: str, __: APIOptions, _user_context: Dict[str, Any] ) -> SearchTagsOK: response = await Querier.get_instance().send_get_request( - NormalisedURLPath("/user/search/tags") + NormalisedURLPath("/user/search/tags"), None, _user_context ) return SearchTagsOK(tags=response["tags"]) diff --git a/supertokens_python/recipe/dashboard/api/signin.py b/supertokens_python/recipe/dashboard/api/signin.py index ab5d20f11..80c678a0e 100644 --- a/supertokens_python/recipe/dashboard/api/signin.py +++ b/supertokens_python/recipe/dashboard/api/signin.py @@ -40,6 +40,7 @@ async def handle_emailpassword_signin_api( response = await Querier.get_instance().send_post_request( NormalisedURLPath("/recipe/dashboard/signin"), {"email": email, "password": password}, + user_context=_user_context, ) if "status" in response and response["status"] == "OK": diff --git a/supertokens_python/recipe/dashboard/api/signout.py b/supertokens_python/recipe/dashboard/api/signout.py index b4b29a191..2eb6bc351 100644 --- a/supertokens_python/recipe/dashboard/api/signout.py +++ b/supertokens_python/recipe/dashboard/api/signout.py @@ -42,5 +42,6 @@ async def handle_emailpassword_signout_api( await Querier.get_instance().send_delete_request( NormalisedURLPath("/recipe/dashboard/session"), {"sessionId": session_id_form_auth_header}, + user_context=_user_context, ) return SignOutOK() diff --git a/supertokens_python/recipe/dashboard/api/userdetails/user_delete.py b/supertokens_python/recipe/dashboard/api/userdetails/user_delete.py index 8f7f2d74a..44e59e427 100644 --- a/supertokens_python/recipe/dashboard/api/userdetails/user_delete.py +++ b/supertokens_python/recipe/dashboard/api/userdetails/user_delete.py @@ -16,6 +16,6 @@ async def handle_user_delete( if user_id is None: raise_bad_input_exception("Missing required parameter 'userId'") - await Supertokens.get_instance().delete_user(user_id) + await Supertokens.get_instance().delete_user(user_id, _user_context) return UserDeleteAPIResponse() diff --git a/supertokens_python/recipe/dashboard/api/users_count_get.py b/supertokens_python/recipe/dashboard/api/users_count_get.py index dbbd15bd6..8a0f9005e 100644 --- a/supertokens_python/recipe/dashboard/api/users_count_get.py +++ b/supertokens_python/recipe/dashboard/api/users_count_get.py @@ -31,5 +31,8 @@ async def handle_users_count_get_api( _api_options: APIOptions, _user_context: Dict[str, Any], ) -> UserCountGetAPIResponse: - count = await Supertokens.get_instance().get_user_count(None, tenant_id) + count = await Supertokens.get_instance().get_user_count( + None, + tenant_id, + ) return UserCountGetAPIResponse(count=count) diff --git a/supertokens_python/recipe/dashboard/api/users_get.py b/supertokens_python/recipe/dashboard/api/users_get.py index 34a6d83d4..949e569da 100644 --- a/supertokens_python/recipe/dashboard/api/users_get.py +++ b/supertokens_python/recipe/dashboard/api/users_get.py @@ -61,6 +61,7 @@ async def handle_users_get_api( pagination_token=pagination_token, include_recipe_ids=None, query=api_options.request.get_query_params(), + user_context=user_context, ) # user metadata bulk fetch with batches: diff --git a/supertokens_python/recipe/dashboard/recipe_implementation.py b/supertokens_python/recipe/dashboard/recipe_implementation.py index 5b97cd030..1db47ef79 100644 --- a/supertokens_python/recipe/dashboard/recipe_implementation.py +++ b/supertokens_python/recipe/dashboard/recipe_implementation.py @@ -51,6 +51,7 @@ async def should_allow_access( await Querier.get_instance().send_post_request( NormalisedURLPath("/recipe/dashboard/session/verify"), {"sessionId": auth_header_value}, + user_context=user_context, ) ) if session_verification_response.get("status") != "OK": diff --git a/supertokens_python/recipe/emailpassword/recipe_implementation.py b/supertokens_python/recipe/emailpassword/recipe_implementation.py index 3e702e374..3e59fd309 100644 --- a/supertokens_python/recipe/emailpassword/recipe_implementation.py +++ b/supertokens_python/recipe/emailpassword/recipe_implementation.py @@ -55,7 +55,7 @@ async def get_user_by_id( ) -> Union[User, None]: params = {"userId": user_id} response = await self.querier.send_get_request( - NormalisedURLPath("/recipe/user"), params + NormalisedURLPath("/recipe/user"), params, user_context ) if "status" in response and response["status"] == "OK": return User( @@ -71,7 +71,7 @@ async def get_user_by_email( ) -> Union[User, None]: params = {"email": email} response = await self.querier.send_get_request( - NormalisedURLPath(f"{tenant_id}/recipe/user"), params + NormalisedURLPath(f"{tenant_id}/recipe/user"), params, user_context ) if "status" in response and response["status"] == "OK": return User( @@ -87,7 +87,9 @@ async def create_reset_password_token( ) -> Union[CreateResetPasswordOkResult, CreateResetPasswordWrongUserIdError]: data = {"userId": user_id} response = await self.querier.send_post_request( - NormalisedURLPath(f"{tenant_id}/recipe/user/password/reset/token"), data + NormalisedURLPath(f"{tenant_id}/recipe/user/password/reset/token"), + data, + user_context=user_context, ) if "status" in response and response["status"] == "OK": return CreateResetPasswordOkResult(response["token"]) @@ -104,7 +106,9 @@ async def reset_password_using_token( ]: data = {"method": "token", "token": token, "newPassword": new_password} response = await self.querier.send_post_request( - NormalisedURLPath(f"{tenant_id}/recipe/user/password/reset"), data + NormalisedURLPath(f"{tenant_id}/recipe/user/password/reset"), + data, + user_context=user_context, ) if "status" not in response or response["status"] != "OK": return ResetPasswordUsingTokenInvalidTokenError() @@ -118,7 +122,9 @@ async def sign_in( ) -> Union[SignInOkResult, SignInWrongCredentialsError]: data = {"password": password, "email": email} response = await self.querier.send_post_request( - NormalisedURLPath(f"{tenant_id}/recipe/signin"), data + NormalisedURLPath(f"{tenant_id}/recipe/signin"), + data, + user_context=user_context, ) if "status" in response and response["status"] == "OK": return SignInOkResult( @@ -136,7 +142,9 @@ async def sign_up( ) -> Union[SignUpOkResult, SignUpEmailAlreadyExistsError]: data = {"password": password, "email": email} response = await self.querier.send_post_request( - NormalisedURLPath(f"{tenant_id}/recipe/signup"), data + NormalisedURLPath(f"{tenant_id}/recipe/signup"), + data, + user_context=user_context, ) if "status" in response and response["status"] == "OK": return SignUpOkResult( @@ -181,7 +189,9 @@ async def update_email_or_password( return UpdateEmailOrPasswordPasswordPolicyViolationError(error) data = {"password": password, **data} response = await self.querier.send_put_request( - NormalisedURLPath("/recipe/user"), data + NormalisedURLPath("/recipe/user"), + data, + user_context=user_context, ) if "status" in response and response["status"] == "OK": return UpdateEmailOrPasswordOkResult() diff --git a/supertokens_python/recipe/emailverification/recipe_implementation.py b/supertokens_python/recipe/emailverification/recipe_implementation.py index 5ec6e5ac3..c4b17f237 100644 --- a/supertokens_python/recipe/emailverification/recipe_implementation.py +++ b/supertokens_python/recipe/emailverification/recipe_implementation.py @@ -48,7 +48,9 @@ async def create_email_verification_token( ]: data = {"userId": user_id, "email": email} response = await self.querier.send_post_request( - NormalisedURLPath(f"{tenant_id}/recipe/user/email/verify/token"), data + NormalisedURLPath(f"{tenant_id}/recipe/user/email/verify/token"), + data, + user_context, ) if "status" in response and response["status"] == "OK": return CreateEmailVerificationTokenOkResult(response["token"]) @@ -59,7 +61,9 @@ async def verify_email_using_token( ) -> Union[VerifyEmailUsingTokenOkResult, VerifyEmailUsingTokenInvalidTokenError]: data = {"method": "token", "token": token} response = await self.querier.send_post_request( - NormalisedURLPath(f"{tenant_id}/recipe/user/email/verify"), data + NormalisedURLPath(f"{tenant_id}/recipe/user/email/verify"), + data, + user_context, ) if "status" in response and response["status"] == "OK": return VerifyEmailUsingTokenOkResult( @@ -72,7 +76,7 @@ async def is_email_verified( ) -> bool: params = {"userId": user_id, "email": email} response = await self.querier.send_get_request( - NormalisedURLPath("/recipe/user/email/verify"), params + NormalisedURLPath("/recipe/user/email/verify"), params, user_context ) return response["isVerified"] @@ -83,6 +87,7 @@ async def revoke_email_verification_tokens( await self.querier.send_post_request( NormalisedURLPath(f"{tenant_id}/recipe/user/email/verify/token/remove"), data, + user_context, ) return RevokeEmailVerificationTokensOkResult() @@ -91,6 +96,6 @@ async def unverify_email( ) -> UnverifyEmailOkResult: data = {"userId": user_id, "email": email} await self.querier.send_post_request( - NormalisedURLPath("/recipe/user/email/verify/remove"), data + NormalisedURLPath("/recipe/user/email/verify/remove"), data, user_context ) return UnverifyEmailOkResult() diff --git a/supertokens_python/recipe/jwt/recipe_implementation.py b/supertokens_python/recipe/jwt/recipe_implementation.py index bfc32bc2e..f265d3c81 100644 --- a/supertokens_python/recipe/jwt/recipe_implementation.py +++ b/supertokens_python/recipe/jwt/recipe_implementation.py @@ -62,7 +62,9 @@ async def create_jwt( "jwksDomain": self.app_info.api_domain.get_as_string_dangerous(), } response = await self.querier.send_post_request( - NormalisedURLPath("/recipe/jwt"), data + NormalisedURLPath("/recipe/jwt"), + data, + user_context=user_context, ) if response["status"] == "OK": @@ -71,7 +73,9 @@ async def create_jwt( async def get_jwks(self, user_context: Dict[str, Any]) -> GetJWKSResult: response = await self.querier.send_get_request( - NormalisedURLPath("/.well-known/jwks.json"), {} + NormalisedURLPath("/.well-known/jwks.json"), + {}, + user_context=user_context, ) validity_in_secs = DEFAULT_JWKS_MAX_AGE diff --git a/supertokens_python/recipe/multitenancy/recipe_implementation.py b/supertokens_python/recipe/multitenancy/recipe_implementation.py index 782a36ea1..30a1105b4 100644 --- a/supertokens_python/recipe/multitenancy/recipe_implementation.py +++ b/supertokens_python/recipe/multitenancy/recipe_implementation.py @@ -143,6 +143,7 @@ async def create_or_update_tenant( "tenantId": tenant_id, **(config.to_json() if config is not None else {}), }, + user_context=user_context, ) return CreateOrUpdateTenantOkResult( created_new=response["createdNew"], @@ -154,6 +155,7 @@ async def delete_tenant( response = await self.querier.send_post_request( NormalisedURLPath("/recipe/multitenancy/tenant/remove"), {"tenantId": tenant_id}, + user_context=user_context, ) return DeleteTenantOkResult( did_exist=response["didExist"], @@ -166,6 +168,8 @@ async def get_tenant( NormalisedURLPath( f"{tenant_id or DEFAULT_TENANT_ID}/recipe/multitenancy/tenant" ), + None, + user_context=user_context, ) if res["status"] == "TENANT_NOT_FOUND_ERROR": @@ -186,6 +190,7 @@ async def list_all_tenants( response = await self.querier.send_get_request( NormalisedURLPath("/recipe/multitenancy/tenant/list"), {}, + user_context=user_context, ) tenant_items: List[ListAllTenantsItem] = [] @@ -220,6 +225,7 @@ async def create_or_update_third_party_config( "config": config.to_json(), "skipValidation": skip_validation is True, }, + user_context=user_context, ) return CreateOrUpdateThirdPartyConfigOkResult( @@ -239,6 +245,7 @@ async def delete_third_party_config( { "thirdPartyId": third_party_id, }, + user_context=user_context, ) return DeleteThirdPartyConfigOkResult( @@ -261,6 +268,7 @@ async def associate_user_to_tenant( { "userId": user_id, }, + user_context=user_context, ) if response["status"] == "OK": @@ -294,6 +302,7 @@ async def dissociate_user_from_tenant( { "userId": user_id, }, + user_context=user_context, ) return DisassociateUserFromTenantOkResult( diff --git a/supertokens_python/recipe/passwordless/recipe_implementation.py b/supertokens_python/recipe/passwordless/recipe_implementation.py index 44b3c8d72..19a75b97c 100644 --- a/supertokens_python/recipe/passwordless/recipe_implementation.py +++ b/supertokens_python/recipe/passwordless/recipe_implementation.py @@ -63,7 +63,9 @@ async def create_code( if phone_number is not None: data = {**data, "phoneNumber": phone_number} result = await self.querier.send_post_request( - NormalisedURLPath(f"{tenant_id}/recipe/signinup/code"), data + NormalisedURLPath(f"{tenant_id}/recipe/signinup/code"), + data, + user_context=user_context, ) return CreateCodeOkResult( pre_auth_session_id=result["preAuthSessionId"], @@ -90,7 +92,9 @@ async def create_new_code_for_device( if user_input_code is not None: data = {**data, "userInputCode": user_input_code} result = await self.querier.send_post_request( - NormalisedURLPath(f"{tenant_id}/recipe/signinup/code"), data + NormalisedURLPath(f"{tenant_id}/recipe/signinup/code"), + data, + user_context=user_context, ) if result["status"] == "RESTART_FLOW_ERROR": return CreateNewCodeForDeviceRestartFlowError() @@ -126,7 +130,9 @@ async def consume_code( else: data = {**data, "linkCode": link_code} result = await self.querier.send_post_request( - NormalisedURLPath(f"{tenant_id}/recipe/signinup/code/consume"), data + NormalisedURLPath(f"{tenant_id}/recipe/signinup/code/consume"), + data, + user_context=user_context, ) if result["status"] == "OK": email = None @@ -160,7 +166,9 @@ async def get_user_by_id( ) -> Union[User, None]: param = {"userId": user_id} result = await self.querier.send_get_request( - NormalisedURLPath("/recipe/user"), param + NormalisedURLPath("/recipe/user"), + param, + user_context=user_context, ) if result["status"] == "OK": email = None @@ -183,7 +191,9 @@ async def get_user_by_email( ) -> Union[User, None]: param = {"email": email} result = await self.querier.send_get_request( - NormalisedURLPath(f"{tenant_id}/recipe/user"), param + NormalisedURLPath(f"{tenant_id}/recipe/user"), + param, + user_context=user_context, ) if result["status"] == "OK": email_resp = None @@ -206,7 +216,9 @@ async def get_user_by_phone_number( ) -> Union[User, None]: param = {"phoneNumber": phone_number} result = await self.querier.send_get_request( - NormalisedURLPath(f"{tenant_id}/recipe/user"), param + NormalisedURLPath(f"{tenant_id}/recipe/user"), + param, + user_context=user_context, ) if result["status"] == "OK": email_resp = None @@ -242,7 +254,9 @@ async def update_user( if phone_number is not None: data = {**data, "phoneNumber": phone_number} result = await self.querier.send_put_request( - NormalisedURLPath("/recipe/user"), data + NormalisedURLPath("/recipe/user"), + data, + user_context=user_context, ) if result["status"] == "OK": return UpdateUserOkResult() @@ -257,7 +271,9 @@ async def delete_email_for_user( ) -> Union[DeleteUserInfoOkResult, DeleteUserInfoUnknownUserIdError]: data = {"userId": user_id, "email": None} result = await self.querier.send_put_request( - NormalisedURLPath("/recipe/user"), data + NormalisedURLPath("/recipe/user"), + data, + user_context=user_context, ) if result["status"] == "OK": return DeleteUserInfoOkResult() @@ -272,7 +288,9 @@ async def delete_phone_number_for_user( ) -> Union[DeleteUserInfoOkResult, DeleteUserInfoUnknownUserIdError]: data = {"userId": user_id, "phoneNumber": None} result = await self.querier.send_put_request( - NormalisedURLPath("/recipe/user"), data + NormalisedURLPath("/recipe/user"), + data, + user_context=user_context, ) if result["status"] == "OK": return DeleteUserInfoOkResult() @@ -295,7 +313,9 @@ async def revoke_all_codes( if phone_number is not None: data = {**data, "email": phone_number} await self.querier.send_post_request( - NormalisedURLPath(f"{tenant_id}/recipe/signinup/codes/remove"), data + NormalisedURLPath(f"{tenant_id}/recipe/signinup/codes/remove"), + data, + user_context=user_context, ) return RevokeAllCodesOkResult() @@ -304,7 +324,9 @@ async def revoke_code( ) -> RevokeCodeOkResult: data = {"codeId": code_id} await self.querier.send_post_request( - NormalisedURLPath(f"{tenant_id}/recipe/signinup/code/remove"), data + NormalisedURLPath(f"{tenant_id}/recipe/signinup/code/remove"), + data, + user_context=user_context, ) return RevokeCodeOkResult() @@ -313,7 +335,9 @@ async def list_codes_by_email( ) -> List[DeviceType]: param = {"email": email} result = await self.querier.send_get_request( - NormalisedURLPath(f"{tenant_id}/recipe/signinup/codes"), param + NormalisedURLPath(f"{tenant_id}/recipe/signinup/codes"), + param, + user_context=user_context, ) devices: List[DeviceType] = [] if "devices" in result: @@ -352,7 +376,9 @@ async def list_codes_by_phone_number( ) -> List[DeviceType]: param = {"phoneNumber": phone_number} result = await self.querier.send_get_request( - NormalisedURLPath(f"{tenant_id}/recipe/signinup/codes"), param + NormalisedURLPath(f"{tenant_id}/recipe/signinup/codes"), + param, + user_context=user_context, ) devices: List[DeviceType] = [] if "devices" in result: @@ -391,7 +417,9 @@ async def list_codes_by_device_id( ) -> Union[DeviceType, None]: param = {"deviceId": device_id} result = await self.querier.send_get_request( - NormalisedURLPath(f"{tenant_id}/recipe/signinup/codes"), param + NormalisedURLPath(f"{tenant_id}/recipe/signinup/codes"), + param, + user_context=user_context, ) if "devices" in result and len(result["devices"]) == 1: codes: List[DeviceCode] = [] @@ -426,7 +454,9 @@ async def list_codes_by_pre_auth_session_id( ) -> Union[DeviceType, None]: param = {"preAuthSessionId": pre_auth_session_id} result = await self.querier.send_get_request( - NormalisedURLPath(f"{tenant_id}/recipe/signinup/codes"), param + NormalisedURLPath(f"{tenant_id}/recipe/signinup/codes"), + param, + user_context=user_context, ) if "devices" in result and len(result["devices"]) == 1: codes: List[DeviceCode] = [] diff --git a/supertokens_python/recipe/session/recipe_implementation.py b/supertokens_python/recipe/session/recipe_implementation.py index 98dc9a023..7a4a3c782 100644 --- a/supertokens_python/recipe/session/recipe_implementation.py +++ b/supertokens_python/recipe/session/recipe_implementation.py @@ -77,6 +77,7 @@ async def create_new_session( disable_anti_csrf is True, access_token_payload, session_data_in_database, + user_context=user_context, ) log_debug_message("createNewSession: Finished") @@ -231,6 +232,7 @@ async def get_session( anti_csrf_token, (anti_csrf_check is not False), (check_database is True), + user_context, ) log_debug_message("getSession: Success!") @@ -293,6 +295,7 @@ async def refresh_session( refresh_token, anti_csrf_token, disable_anti_csrf, + user_context=user_context, ) log_debug_message("refreshSession: Success!") @@ -325,7 +328,9 @@ async def refresh_session( async def revoke_session( self, session_handle: str, user_context: Dict[str, Any] ) -> bool: - return await session_functions.revoke_session(self, session_handle) + return await session_functions.revoke_session( + self, session_handle, user_context + ) async def revoke_all_sessions_for_user( self, @@ -335,7 +340,7 @@ async def revoke_all_sessions_for_user( user_context: Dict[str, Any], ) -> List[str]: return await session_functions.revoke_all_sessions_for_user( - self, user_id, tenant_id, revoke_across_all_tenants + self, user_id, tenant_id, revoke_across_all_tenants, user_context ) async def get_all_session_handles_for_user( @@ -346,18 +351,22 @@ async def get_all_session_handles_for_user( user_context: Dict[str, Any], ) -> List[str]: return await session_functions.get_all_session_handles_for_user( - self, user_id, tenant_id, fetch_across_all_tenants + self, user_id, tenant_id, fetch_across_all_tenants, user_context ) async def revoke_multiple_sessions( self, session_handles: List[str], user_context: Dict[str, Any] ) -> List[str]: - return await session_functions.revoke_multiple_sessions(self, session_handles) + return await session_functions.revoke_multiple_sessions( + self, session_handles, user_context + ) async def get_session_information( self, session_handle: str, user_context: Dict[str, Any] ) -> Union[SessionInformationResult, None]: - return await session_functions.get_session_information(self, session_handle) + return await session_functions.get_session_information( + self, session_handle, user_context + ) async def update_session_data_in_database( self, @@ -366,7 +375,7 @@ async def update_session_data_in_database( user_context: Dict[str, Any], ) -> bool: return await session_functions.update_session_data_in_database( - self, session_handle, new_session_data + self, session_handle, new_session_data, user_context ) async def merge_into_access_token_payload( @@ -393,7 +402,7 @@ async def merge_into_access_token_payload( del new_access_token_payload[k] return await session_functions.update_access_token_payload( - self, session_handle, new_access_token_payload + self, session_handle, new_access_token_payload, user_context ) async def fetch_and_set_claim( @@ -472,6 +481,7 @@ async def regenerate_access_token( response = await self.querier.send_post_request( NormalisedURLPath("/recipe/session/regenerate"), {"accessToken": access_token, "userDataInJWT": new_access_token_payload}, + user_context=user_context, ) if response["status"] == "UNAUTHORISED": return None diff --git a/supertokens_python/recipe/session/session_functions.py b/supertokens_python/recipe/session/session_functions.py index a6bd8dc59..3fac520a8 100644 --- a/supertokens_python/recipe/session/session_functions.py +++ b/supertokens_python/recipe/session/session_functions.py @@ -102,6 +102,7 @@ async def create_new_session( disable_anti_csrf: bool, access_token_payload: Union[None, Dict[str, Any]], session_data_in_database: Union[None, Dict[str, Any]], + user_context: Optional[Dict[str, Any]], ) -> CreateOrRefreshAPIResponse: if session_data_in_database is None: session_data_in_database = {} @@ -120,6 +121,7 @@ async def create_new_session( "useDynamicSigningKey": recipe_implementation.config.use_dynamic_access_token_signing_key, "enableAntiCsrf": enable_anti_csrf, }, + user_context=user_context, ) return CreateOrRefreshAPIResponse( @@ -149,6 +151,7 @@ async def get_session( anti_csrf_token: Union[str, None], do_anti_csrf_check: bool, always_check_core: bool, + user_context: Optional[Dict[str, Any]], ) -> GetSessionAPIResponse: config = recipe_implementation.config access_token_info: Optional[Dict[str, Any]] = None @@ -276,7 +279,9 @@ async def get_session( data["antiCsrfToken"] = anti_csrf_token response = await recipe_implementation.querier.send_post_request( - NormalisedURLPath("/recipe/session/verify"), data + NormalisedURLPath("/recipe/session/verify"), + data, + user_context=user_context, ) if response["status"] == "OK": return GetSessionAPIResponse( @@ -322,6 +327,7 @@ async def refresh_session( refresh_token: str, anti_csrf_token: Union[str, None], disable_anti_csrf: bool, + user_context: Optional[Dict[str, Any]], ) -> CreateOrRefreshAPIResponse: data = { "refreshToken": refresh_token, @@ -345,7 +351,9 @@ async def refresh_session( ) response = await recipe_implementation.querier.send_post_request( - NormalisedURLPath("/recipe/session/refresh"), data + NormalisedURLPath("/recipe/session/refresh"), + data, + user_context=user_context, ) if response["status"] == "OK": return CreateOrRefreshAPIResponse( @@ -385,6 +393,7 @@ async def revoke_all_sessions_for_user( user_id: str, tenant_id: Optional[str], revoke_across_all_tenants: bool, + user_context: Optional[Dict[str, Any]], ) -> List[str]: if tenant_id is None: tenant_id = DEFAULT_TENANT_ID @@ -392,6 +401,7 @@ async def revoke_all_sessions_for_user( response = await recipe_implementation.querier.send_post_request( NormalisedURLPath(f"{tenant_id}/recipe/session/remove"), {"userId": user_id, "revokeAcrossAllTenants": revoke_across_all_tenants}, + user_context=user_context, ) return response["sessionHandlesRevoked"] @@ -401,6 +411,7 @@ async def get_all_session_handles_for_user( user_id: str, tenant_id: Optional[str], fetch_across_all_tenants: bool, + user_context: Optional[Dict[str, Any]], ) -> List[str]: if tenant_id is None: tenant_id = DEFAULT_TENANT_ID @@ -408,25 +419,33 @@ async def get_all_session_handles_for_user( response = await recipe_implementation.querier.send_get_request( NormalisedURLPath(f"{tenant_id}/recipe/session/user"), {"userId": user_id, "fetchAcrossAllTenants": fetch_across_all_tenants}, + user_context=user_context, ) return response["sessionHandles"] async def revoke_session( - recipe_implementation: RecipeImplementation, session_handle: str + recipe_implementation: RecipeImplementation, + session_handle: str, + user_context: Optional[Dict[str, Any]], ) -> bool: response = await recipe_implementation.querier.send_post_request( NormalisedURLPath("/recipe/session/remove"), {"sessionHandles": [session_handle]}, + user_context=user_context, ) return len(response["sessionHandlesRevoked"]) == 1 async def revoke_multiple_sessions( - recipe_implementation: RecipeImplementation, session_handles: List[str] + recipe_implementation: RecipeImplementation, + session_handles: List[str], + user_context: Optional[Dict[str, Any]], ) -> List[str]: response = await recipe_implementation.querier.send_post_request( - NormalisedURLPath("/recipe/session/remove"), {"sessionHandles": session_handles} + NormalisedURLPath("/recipe/session/remove"), + {"sessionHandles": session_handles}, + user_context=user_context, ) return response["sessionHandlesRevoked"] @@ -435,10 +454,12 @@ async def update_session_data_in_database( recipe_implementation: RecipeImplementation, session_handle: str, new_session_data: Dict[str, Any], + user_context: Optional[Dict[str, Any]], ) -> bool: response = await recipe_implementation.querier.send_put_request( NormalisedURLPath("/recipe/session/data"), {"sessionHandle": session_handle, "userDataInDatabase": new_session_data}, + user_context=user_context, ) if response["status"] == "UNAUTHORISED": return False @@ -450,10 +471,12 @@ async def update_access_token_payload( recipe_implementation: RecipeImplementation, session_handle: str, new_access_token_payload: Dict[str, Any], + user_context: Optional[Dict[str, Any]], ) -> bool: response = await recipe_implementation.querier.send_put_request( NormalisedURLPath("/recipe/jwt/data"), {"sessionHandle": session_handle, "userDataInJWT": new_access_token_payload}, + user_context=user_context, ) if response["status"] == "UNAUTHORISED": return False @@ -462,10 +485,14 @@ async def update_access_token_payload( async def get_session_information( - recipe_implementation: RecipeImplementation, session_handle: str + recipe_implementation: RecipeImplementation, + session_handle: str, + user_context: Optional[Dict[str, Any]], ) -> Union[SessionInformationResult, None]: response = await recipe_implementation.querier.send_get_request( - NormalisedURLPath("/recipe/session"), {"sessionHandle": session_handle} + NormalisedURLPath("/recipe/session"), + {"sessionHandle": session_handle}, + user_context=user_context, ) if response["status"] == "OK": return SessionInformationResult( diff --git a/supertokens_python/recipe/thirdparty/recipe_implementation.py b/supertokens_python/recipe/thirdparty/recipe_implementation.py index 726938574..c60a757bc 100644 --- a/supertokens_python/recipe/thirdparty/recipe_implementation.py +++ b/supertokens_python/recipe/thirdparty/recipe_implementation.py @@ -45,7 +45,9 @@ async def get_user_by_id( ) -> Union[User, None]: params = {"userId": user_id} response = await self.querier.send_get_request( - NormalisedURLPath("/recipe/user"), params + NormalisedURLPath("/recipe/user"), + params, + user_context=user_context, ) if "status" in response and response["status"] == "OK": return User( @@ -64,7 +66,9 @@ async def get_users_by_email( self, email: str, tenant_id: str, user_context: Dict[str, Any] ) -> List[User]: response = await self.querier.send_get_request( - NormalisedURLPath(f"{tenant_id}/recipe/users/by-email"), {"email": email} + NormalisedURLPath(f"{tenant_id}/recipe/users/by-email"), + {"email": email}, + user_context=user_context, ) users: List[User] = [] users_list: List[Dict[str, Any]] = ( @@ -96,7 +100,9 @@ async def get_user_by_thirdparty_info( "thirdPartyUserId": third_party_user_id, } response = await self.querier.send_get_request( - NormalisedURLPath(f"{tenant_id}/recipe/user"), params + NormalisedURLPath(f"{tenant_id}/recipe/user"), + params, + user_context=user_context, ) if "status" in response and response["status"] == "OK": return User( @@ -127,7 +133,9 @@ async def sign_in_up( "email": {"id": email}, } response = await self.querier.send_post_request( - NormalisedURLPath(f"{tenant_id}/recipe/signinup"), data + NormalisedURLPath(f"{tenant_id}/recipe/signinup"), + data, + user_context=user_context, ) return SignInUpOkResult( User( @@ -159,7 +167,9 @@ async def manually_create_or_update_user( "email": {"id": email}, } response = await self.querier.send_post_request( - NormalisedURLPath(f"{tenant_id}/recipe/signinup"), data + NormalisedURLPath(f"{tenant_id}/recipe/signinup"), + data, + user_context=user_context, ) return ManuallyCreateOrUpdateUserOkResult( User( diff --git a/supertokens_python/recipe/usermetadata/recipe_implementation.py b/supertokens_python/recipe/usermetadata/recipe_implementation.py index 7fa51696c..7485ddbe5 100644 --- a/supertokens_python/recipe/usermetadata/recipe_implementation.py +++ b/supertokens_python/recipe/usermetadata/recipe_implementation.py @@ -31,7 +31,9 @@ async def get_user_metadata( ) -> MetadataResult: params = {"userId": user_id} response = await self.querier.send_get_request( - NormalisedURLPath("/recipe/user/metadata"), params + NormalisedURLPath("/recipe/user/metadata"), + params, + user_context=user_context, ) return MetadataResult(metadata=response["metadata"]) @@ -43,7 +45,9 @@ async def update_user_metadata( ) -> MetadataResult: params = {"userId": user_id, "metadataUpdate": metadata_update} response = await self.querier.send_put_request( - NormalisedURLPath("/recipe/user/metadata"), params + NormalisedURLPath("/recipe/user/metadata"), + params, + user_context=user_context, ) return MetadataResult(metadata=response["metadata"]) @@ -52,6 +56,8 @@ async def clear_user_metadata( ) -> ClearUserMetadataResult: params = {"userId": user_id} await self.querier.send_post_request( - NormalisedURLPath("/recipe/user/metadata/remove"), params + NormalisedURLPath("/recipe/user/metadata/remove"), + params, + user_context=user_context, ) return ClearUserMetadataResult() diff --git a/supertokens_python/recipe/userroles/recipe_implementation.py b/supertokens_python/recipe/userroles/recipe_implementation.py index c56aab825..955a6ae9d 100644 --- a/supertokens_python/recipe/userroles/recipe_implementation.py +++ b/supertokens_python/recipe/userroles/recipe_implementation.py @@ -50,6 +50,7 @@ async def add_role_to_user( response = await self.querier.send_put_request( NormalisedURLPath(f"{tenant_id}/recipe/user/role"), params, + user_context=user_context, ) if response["status"] == "OK": return AddRoleToUserOkResult( @@ -68,6 +69,7 @@ async def remove_user_role( response = await self.querier.send_post_request( NormalisedURLPath(f"{tenant_id}/recipe/user/role/remove"), params, + user_context=user_context, ) if response["status"] == "OK": return RemoveUserRoleOkResult( @@ -82,6 +84,7 @@ async def get_roles_for_user( response = await self.querier.send_get_request( NormalisedURLPath(f"{tenant_id}/recipe/user/roles"), params, + user_context=user_context, ) return GetRolesForUserOkResult(roles=response["roles"]) @@ -92,6 +95,7 @@ async def get_users_that_have_role( response = await self.querier.send_get_request( NormalisedURLPath(f"{tenant_id}/recipe/role/users"), params, + user_context=user_context, ) if response["status"] == "OK": return GetUsersThatHaveRoleOkResult(users=response["users"]) @@ -102,7 +106,9 @@ async def create_new_role_or_add_permissions( ) -> CreateNewRoleOrAddPermissionsOkResult: params = {"role": role, "permissions": permissions} response = await self.querier.send_put_request( - NormalisedURLPath("/recipe/role"), params + NormalisedURLPath("/recipe/role"), + params, + user_context=user_context, ) return CreateNewRoleOrAddPermissionsOkResult( created_new_role=response["createdNewRole"] @@ -113,7 +119,9 @@ async def get_permissions_for_role( ) -> Union[GetPermissionsForRoleOkResult, UnknownRoleError]: params = {"role": role} response = await self.querier.send_get_request( - NormalisedURLPath("/recipe/role/permissions"), params + NormalisedURLPath("/recipe/role/permissions"), + params, + user_context=user_context, ) if response["status"] == "OK": return GetPermissionsForRoleOkResult(permissions=response["permissions"]) @@ -124,7 +132,9 @@ async def remove_permissions_from_role( ) -> Union[RemovePermissionsFromRoleOkResult, UnknownRoleError]: params = {"role": role, "permissions": permissions} response = await self.querier.send_post_request( - NormalisedURLPath("/recipe/role/permissions/remove"), params + NormalisedURLPath("/recipe/role/permissions/remove"), + params, + user_context=user_context, ) if response["status"] == "OK": return RemovePermissionsFromRoleOkResult() @@ -135,7 +145,9 @@ async def get_roles_that_have_permission( ) -> GetRolesThatHavePermissionOkResult: params = {"permission": permission} response = await self.querier.send_get_request( - NormalisedURLPath("/recipe/permission/roles"), params + NormalisedURLPath("/recipe/permission/roles"), + params, + user_context=user_context, ) return GetRolesThatHavePermissionOkResult(roles=response["roles"]) @@ -144,13 +156,17 @@ async def delete_role( ) -> DeleteRoleOkResult: params = {"role": role} response = await self.querier.send_post_request( - NormalisedURLPath("/recipe/role/remove"), params + NormalisedURLPath("/recipe/role/remove"), + params, + user_context=user_context, ) return DeleteRoleOkResult(did_role_exist=response["didRoleExist"]) async def get_all_roles(self, user_context: Dict[str, Any]) -> GetAllRolesOkResult: params = {} response = await self.querier.send_get_request( - NormalisedURLPath("/recipe/roles"), params + NormalisedURLPath("/recipe/roles"), + params, + user_context=user_context, ) return GetAllRolesOkResult(roles=response["roles"]) diff --git a/supertokens_python/supertokens.py b/supertokens_python/supertokens.py index 31bed9b79..891f20463 100644 --- a/supertokens_python/supertokens.py +++ b/supertokens_python/supertokens.py @@ -15,7 +15,7 @@ from __future__ import annotations from os import environ -from typing import TYPE_CHECKING, Any, Callable, Dict, List, Optional, Set, Union +from typing import TYPE_CHECKING, Any, Callable, Dict, List, Optional, Set, Union, Tuple from typing_extensions import Literal @@ -24,7 +24,6 @@ log_debug_message, enable_debug_logging, ) - from .constants import FDI_KEY_HEADER, RID_KEY_HEADER, USER_COUNT, USER_DELETE, USERS from .exceptions import SuperTokensError from .interfaces import ( @@ -63,10 +62,32 @@ class SupertokensConfig: def __init__( - self, connection_uri: str, api_key: Union[str, None] = None + self, + connection_uri: str, + api_key: Union[str, None] = None, + network_interceptor: Optional[ + Callable[ + [ + str, + str, + Dict[str, Any], + Optional[Dict[str, Any]], + Optional[Dict[str, Any]], + Optional[Dict[str, Any]], + ], + Tuple[ + str, + str, + Dict[str, Any], + Optional[Dict[str, Any]], + Optional[Dict[str, Any]], + ], + ] + ] = None, ): # We keep this = None here because this is directly used by the user. self.connection_uri = connection_uri self.api_key = api_key + self.network_interceptor = network_interceptor class Host: @@ -186,7 +207,9 @@ def __init__( filter(lambda x: x != "", supertokens_config.connection_uri.split(";")), ) ) - Querier.init(hosts, supertokens_config.api_key) + Querier.init( + hosts, supertokens_config.api_key, supertokens_config.network_interceptor + ) if len(recipe_list) == 0: raise_general_exception( @@ -270,6 +293,7 @@ async def get_user_count( # pylint: disable=no-self-use self, include_recipe_ids: Union[None, List[str]], tenant_id: Optional[str] = None, + user_context: Optional[Dict[str, Any]] = None, ) -> int: querier = Querier.get_instance(None) include_recipe_ids_str = None @@ -282,18 +306,25 @@ async def get_user_count( # pylint: disable=no-self-use "includeRecipeIds": include_recipe_ids_str, "includeAllTenants": tenant_id is None, }, + user_context=user_context, ) return int(response["count"]) - async def delete_user(self, user_id: str) -> None: # pylint: disable=no-self-use + async def delete_user( # pylint: disable=no-self-use + self, + user_id: str, + user_context: Optional[Dict[str, Any]], + ) -> None: querier = Querier.get_instance(None) cdi_version = await querier.get_api_version() if is_version_gte(cdi_version, "2.10"): await querier.send_post_request( - NormalisedURLPath(USER_DELETE), {"userId": user_id} + NormalisedURLPath(USER_DELETE), + {"userId": user_id}, + user_context=user_context, ) return None @@ -306,7 +337,8 @@ async def get_users( # pylint: disable=no-self-use limit: Union[int, None], pagination_token: Union[str, None], include_recipe_ids: Union[None, List[str]], - query: Union[Dict[str, str], None] = None, + query: Union[Dict[str, str], None], + user_context: Optional[Dict[str, Any]], ) -> UsersResponse: from supertokens_python.recipe.multitenancy.constants import DEFAULT_TENANT_ID @@ -329,7 +361,7 @@ async def get_users( # pylint: disable=no-self-use tenant_id = DEFAULT_TENANT_ID response = await querier.send_get_request( - NormalisedURLPath(f"/{tenant_id}{USERS}"), params + NormalisedURLPath(f"/{tenant_id}{USERS}"), params, user_context=user_context ) next_pagination_token = None if "nextPaginationToken" in response: @@ -368,8 +400,9 @@ async def create_user_id_mapping( # pylint: disable=no-self-use self, supertokens_user_id: str, external_user_id: str, - external_user_id_info: Optional[str] = None, - force: Optional[bool] = None, + external_user_id_info: Optional[str], + force: Optional[bool], + user_context: Optional[Dict[str, Any]], ) -> Union[ CreateUserIdMappingOkResult, UnknownSupertokensUserIDError, @@ -389,7 +422,7 @@ async def create_user_id_mapping( # pylint: disable=no-self-use body["force"] = force res = await querier.send_post_request( - NormalisedURLPath("/recipe/userid/map"), body + NormalisedURLPath("/recipe/userid/map"), body, user_context=user_context ) if res["status"] == "OK": return CreateUserIdMappingOkResult() @@ -408,7 +441,8 @@ async def create_user_id_mapping( # pylint: disable=no-self-use async def get_user_id_mapping( # pylint: disable=no-self-use self, user_id: str, - user_id_type: Optional[UserIDTypes] = None, + user_id_type: Optional[UserIDTypes], + user_context: Optional[Dict[str, Any]], ) -> Union[GetUserIdMappingOkResult, UnknownMappingError]: querier = Querier.get_instance(None) @@ -423,6 +457,7 @@ async def get_user_id_mapping( # pylint: disable=no-self-use res = await querier.send_get_request( NormalisedURLPath("/recipe/userid/map"), body, + user_context=user_context, ) if res["status"] == "OK": return GetUserIdMappingOkResult( @@ -440,8 +475,9 @@ async def get_user_id_mapping( # pylint: disable=no-self-use async def delete_user_id_mapping( # pylint: disable=no-self-use self, user_id: str, - user_id_type: Optional[UserIDTypes] = None, - force: Optional[bool] = None, + user_id_type: Optional[UserIDTypes], + force: Optional[bool], + user_context: Optional[Dict[str, Any]], ) -> DeleteUserIdMappingOkResult: querier = Querier.get_instance(None) @@ -455,7 +491,9 @@ async def delete_user_id_mapping( # pylint: disable=no-self-use if force: body["force"] = force res = await querier.send_post_request( - NormalisedURLPath("/recipe/userid/map/remove"), body + NormalisedURLPath("/recipe/userid/map/remove"), + body, + user_context=user_context, ) if res["status"] == "OK": return DeleteUserIdMappingOkResult( @@ -469,8 +507,9 @@ async def delete_user_id_mapping( # pylint: disable=no-self-use async def update_or_delete_user_id_mapping_info( # pylint: disable=no-self-use self, user_id: str, - user_id_type: Optional[UserIDTypes] = None, - external_user_id_info: Optional[str] = None, + user_id_type: Optional[UserIDTypes], + external_user_id_info: Optional[str], + user_context: Optional[Dict[str, Any]], ) -> Union[UpdateOrDeleteUserIdMappingInfoOkResult, UnknownMappingError]: querier = Querier.get_instance(None) @@ -484,6 +523,7 @@ async def update_or_delete_user_id_mapping_info( # pylint: disable=no-self-use "userIdType": user_id_type, "externalUserIdInfo": external_user_id_info, }, + user_context=user_context, ) if res["status"] == "OK": return UpdateOrDeleteUserIdMappingInfoOkResult() diff --git a/supertokens_python/syncio/__init__.py b/supertokens_python/syncio/__init__.py index 06980dfab..b1557b074 100644 --- a/supertokens_python/syncio/__init__.py +++ b/supertokens_python/syncio/__init__.py @@ -11,7 +11,7 @@ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. -from typing import Dict, List, Optional, Union +from typing import Dict, List, Optional, Union, Any from supertokens_python import Supertokens from supertokens_python.async_to_sync_wrapper import sync @@ -34,10 +34,17 @@ def get_users_oldest_first( pagination_token: Union[str, None] = None, include_recipe_ids: Union[None, List[str]] = None, query: Union[None, Dict[str, str]] = None, + user_context: Optional[Dict[str, Any]] = None, ) -> UsersResponse: return sync( Supertokens.get_instance().get_users( - tenant_id, "ASC", limit, pagination_token, include_recipe_ids, query + tenant_id, + "ASC", + limit, + pagination_token, + include_recipe_ids, + query, + user_context, ) ) @@ -48,10 +55,17 @@ def get_users_newest_first( pagination_token: Union[str, None] = None, include_recipe_ids: Union[None, List[str]] = None, query: Union[None, Dict[str, str]] = None, + user_context: Optional[Dict[str, Any]] = None, ) -> UsersResponse: return sync( Supertokens.get_instance().get_users( - tenant_id, "DESC", limit, pagination_token, include_recipe_ids, query + tenant_id, + "DESC", + limit, + pagination_token, + include_recipe_ids, + query, + user_context, ) ) @@ -59,20 +73,25 @@ def get_users_newest_first( def get_user_count( include_recipe_ids: Union[None, List[str]] = None, tenant_id: Optional[str] = None, + user_context: Optional[Dict[str, Any]] = None, ) -> int: return sync( - Supertokens.get_instance().get_user_count(include_recipe_ids, tenant_id) + Supertokens.get_instance().get_user_count( + include_recipe_ids, tenant_id, user_context + ) ) -def delete_user(user_id: str) -> None: - return sync(Supertokens.get_instance().delete_user(user_id)) +def delete_user(user_id: str, user_context: Optional[Dict[str, Any]] = None) -> None: + return sync(Supertokens.get_instance().delete_user(user_id, user_context)) def create_user_id_mapping( supertokens_user_id: str, external_user_id: str, external_user_id_info: Optional[str] = None, + force: Optional[bool] = None, + user_context: Optional[Dict[str, Any]] = None, ) -> Union[ CreateUserIdMappingOkResult, UnknownSupertokensUserIDError, @@ -80,7 +99,11 @@ def create_user_id_mapping( ]: return sync( Supertokens.get_instance().create_user_id_mapping( - supertokens_user_id, external_user_id, external_user_id_info + supertokens_user_id, + external_user_id, + external_user_id_info, + force=force, + user_context=user_context, ) ) @@ -88,15 +111,25 @@ def create_user_id_mapping( def get_user_id_mapping( user_id: str, user_id_type: Optional[UserIDTypes] = None, + user_context: Optional[Dict[str, Any]] = None, ) -> Union[GetUserIdMappingOkResult, UnknownMappingError]: - return sync(Supertokens.get_instance().get_user_id_mapping(user_id, user_id_type)) + return sync( + Supertokens.get_instance().get_user_id_mapping( + user_id, user_id_type, user_context + ) + ) def delete_user_id_mapping( - user_id: str, user_id_type: Optional[UserIDTypes] = None + user_id: str, + user_id_type: Optional[UserIDTypes] = None, + force: Optional[bool] = None, + user_context: Optional[Dict[str, Any]] = None, ) -> DeleteUserIdMappingOkResult: return sync( - Supertokens.get_instance().delete_user_id_mapping(user_id, user_id_type) + Supertokens.get_instance().delete_user_id_mapping( + user_id, user_id_type, force=force, user_context=user_context + ) ) @@ -104,9 +137,10 @@ def update_or_delete_user_id_mapping_info( user_id: str, user_id_type: Optional[UserIDTypes] = None, external_user_id_info: Optional[str] = None, + user_context: Optional[Dict[str, Any]] = None, ) -> Union[UpdateOrDeleteUserIdMappingInfoOkResult, UnknownMappingError]: return sync( Supertokens.get_instance().update_or_delete_user_id_mapping_info( - user_id, user_id_type, external_user_id_info + user_id, user_id_type, external_user_id_info, user_context ) ) diff --git a/tests/emailpassword/test_emaildelivery.py b/tests/emailpassword/test_emaildelivery.py index a391174b0..762844571 100644 --- a/tests/emailpassword/test_emaildelivery.py +++ b/tests/emailpassword/test_emaildelivery.py @@ -610,7 +610,7 @@ async def test_email_verification_default_backward_compatibility( if not isinstance(s.recipe_implementation, SessionRecipeImplementation): raise Exception("Should never come here") response = await create_new_session( - s.recipe_implementation, "public", user_id, True, {}, {} + s.recipe_implementation, "public", user_id, True, {}, {}, None ) def api_side_effect(request: httpx.Request): @@ -676,7 +676,7 @@ async def test_email_verification_default_backward_compatibility_suppress_error( if not isinstance(s.recipe_implementation, SessionRecipeImplementation): raise Exception("Should never come here") response = await create_new_session( - s.recipe_implementation, "public", user_id, True, {}, {} + s.recipe_implementation, "public", user_id, True, {}, {}, None ) def api_side_effect(request: httpx.Request): @@ -759,7 +759,7 @@ async def send_email( if not isinstance(s.recipe_implementation, SessionRecipeImplementation): raise Exception("Should never come here") response = await create_new_session( - s.recipe_implementation, "public", user_id, True, {}, {} + s.recipe_implementation, "public", user_id, True, {}, {}, None ) res = email_verify_token_request( @@ -830,7 +830,7 @@ async def send_email( if not isinstance(s.recipe_implementation, SessionRecipeImplementation): raise Exception("Should never come here") response = await create_new_session( - s.recipe_implementation, "public", user_id, True, {}, {} + s.recipe_implementation, "public", user_id, True, {}, {}, None ) def api_side_effect(request: httpx.Request): @@ -962,7 +962,7 @@ async def send_email_override( if not isinstance(s.recipe_implementation, SessionRecipeImplementation): raise Exception("Should never come here") response = await create_new_session( - s.recipe_implementation, "public", user_id, True, {}, {} + s.recipe_implementation, "public", user_id, True, {}, {}, None ) resp = email_verify_token_request( diff --git a/tests/sessions/test_access_token_version.py b/tests/sessions/test_access_token_version.py index 244deb484..d20ace55d 100644 --- a/tests/sessions/test_access_token_version.py +++ b/tests/sessions/test_access_token_version.py @@ -137,6 +137,7 @@ async def test_should_validate_v2_tokens_with_check_database_enabled(app: TestCl "userDataInJWT": {}, "userDataInDatabase": {}, }, + None, ) Querier.api_version = None diff --git a/tests/test_network_interceptor.py b/tests/test_network_interceptor.py new file mode 100644 index 000000000..8455af8dd --- /dev/null +++ b/tests/test_network_interceptor.py @@ -0,0 +1,210 @@ +from typing import Dict, Any, Optional + +import pytest +from fastapi import FastAPI +from fastapi.testclient import TestClient +from pytest import fixture, mark +from supertokens_python import InputAppInfo, SupertokensConfig, init +from supertokens_python.exceptions import GeneralError +from supertokens_python.framework.fastapi import get_middleware +from supertokens_python.recipe import session, emailpassword, userroles +from supertokens_python.recipe.emailpassword.asyncio import sign_in, sign_up +from supertokens_python.recipe.userroles.asyncio import get_roles_for_user +from tests.utils import clean_st, reset, setup_st, start_st + + +def setup_function(_): + reset() + clean_st() + setup_st() + + +def teardown_function(_): + reset() + clean_st() + + +@fixture(scope="function") +async def driver_config_client(): + app = FastAPI() + app.add_middleware(get_middleware()) + + return TestClient(app, raise_server_exceptions=False) + + +@mark.asyncio +async def test_network_interceptor_sanity(driver_config_client: TestClient): + is_network_intercepted = False + + def intercept( + url: str, + method: str, + headers: Dict[str, Any], + params: Optional[Dict[str, Any]], + body: Optional[Dict[str, Any]], + _: Optional[Dict[str, Any]], + ): + nonlocal is_network_intercepted + is_network_intercepted = True + return url, method, headers, params, body + + init( + supertokens_config=SupertokensConfig( + "http://localhost:3567", network_interceptor=intercept + ), + app_info=InputAppInfo( + app_name="SuperTokens Demo", + api_domain="http://api.supertokens.io", + website_domain="http://supertokens.io", + api_base_path="/auth", + ), + framework="fastapi", + recipe_list=[ + emailpassword.init(), + session.init(get_token_transfer_method=lambda _, __, ___: "cookie"), + ], + ) + start_st() + resp = driver_config_client.post( + url="/auth/signin", + json={ + "formFields": [ + { + "id": "email", + "value": "testEmail@email.com", + }, + { + "id": "password", + "value": "validPassword123", + }, + ] + }, + ) + + assert is_network_intercepted is True + assert resp.status_code == 200 + + +@mark.asyncio +async def test_network_interceptor_incorrect_core_url(): + is_network_intercepted = False + + def intercept( + url: str, + method: str, + headers: Dict[str, Any], + params: Optional[Dict[str, Any]], + body: Optional[Dict[str, Any]], + __: Optional[Dict[str, Any]], + ): + nonlocal is_network_intercepted + is_network_intercepted = True + url = url + "/incorrect/url" + return url, method, headers, params, body + + init( + supertokens_config=SupertokensConfig( + "http://localhost:3567", network_interceptor=intercept + ), + app_info=InputAppInfo( + app_name="SuperTokens Demo", + api_domain="http://api.supertokens.io", + website_domain="http://supertokens.io", + api_base_path="/auth", + ), + framework="fastapi", + recipe_list=[ + emailpassword.init(), + session.init(get_token_transfer_method=lambda _, __, ___: "cookie"), + ], + ) + start_st() + with pytest.raises(GeneralError) as err: + await sign_up("public", "testEmail@email.com", "validPassword123") + assert "status code: 404" in str(err) + + assert is_network_intercepted is True + + +@mark.asyncio +async def test_network_interceptor_incorrect_query_params(): + is_network_intercepted = False + + def intercept( + url: str, + method: str, + headers: Dict[str, Any], + params: Optional[Dict[str, Any]], + body: Optional[Dict[str, Any]], + __: Optional[Dict[str, Any]], + ): + nonlocal is_network_intercepted + is_network_intercepted = True + params = {} + return url, method, headers, params, body + + init( + supertokens_config=SupertokensConfig( + "http://localhost:3567", network_interceptor=intercept + ), + app_info=InputAppInfo( + app_name="SuperTokens Demo", + api_domain="http://api.supertokens.io", + website_domain="http://supertokens.io", + api_base_path="/auth", + ), + framework="fastapi", + recipe_list=[ + userroles.init(), + emailpassword.init(), + session.init(get_token_transfer_method=lambda _, __, ___: "cookie"), + ], + ) + start_st() + with pytest.raises(GeneralError) as err: + await get_roles_for_user("public", "someUserId") + assert "status code: 400" in str(err) + + assert is_network_intercepted is True + + +@mark.asyncio +async def test_network_interceptor_incorrect_request_body(): + is_network_intercepted = False + + def intercept( + url: str, + method: str, + headers: Dict[str, Any], + params: Optional[Dict[str, Any]], + _: Optional[Dict[str, Any]], + __: Optional[Dict[str, Any]], + ): + nonlocal is_network_intercepted + is_network_intercepted = True + newBody = {"newKey": "newValue"} + return url, method, headers, params, newBody + + init( + supertokens_config=SupertokensConfig( + "http://localhost:3567", network_interceptor=intercept + ), + app_info=InputAppInfo( + app_name="SuperTokens Demo", + api_domain="http://api.supertokens.io", + website_domain="http://supertokens.io", + api_base_path="/auth", + ), + framework="fastapi", + recipe_list=[ + emailpassword.init(), + session.init(get_token_transfer_method=lambda _, __, ___: "cookie"), + ], + ) + start_st() + + with pytest.raises(GeneralError) as err: + await sign_in("public", "testEmail@email.com", "validPassword123") + assert "status code: 400" in str(err) + + assert is_network_intercepted is True diff --git a/tests/test_querier.py b/tests/test_querier.py index d8eb8bd19..82e529d4d 100644 --- a/tests/test_querier.py +++ b/tests/test_querier.py @@ -78,7 +78,7 @@ def api2_side_effect(_: httpx.Request): api3 = mocker.get("http://localhost:6789/api3").mock(httpx.Response(200)) try: - await q.send_get_request(NormalisedURLPath("/api1"), {}) + await q.send_get_request(NormalisedURLPath("/api1"), None, None) except Exception as e: if "with status code: 429" in str( e @@ -87,8 +87,8 @@ def api2_side_effect(_: httpx.Request): else: raise e - await q.send_get_request(NormalisedURLPath("/api2"), {}) - await q.send_get_request(NormalisedURLPath("/api3"), {}) + await q.send_get_request(NormalisedURLPath("/api2"), None, None) + await q.send_get_request(NormalisedURLPath("/api3"), None, None) # 1 initial request + 5 retries assert api1.call_count == 6 @@ -132,7 +132,7 @@ def api_side_effect(r: httpx.Request): async def call_api(id_: int): try: - await q.send_get_request(NormalisedURLPath("/api"), {"id": id_}) + await q.send_get_request(NormalisedURLPath("/api"), {"id": id_}, None) except Exception as e: if "with status code: 429" in str(e): pass @@ -166,7 +166,7 @@ async def test_querier_text_and_headers(): httpx.Response(200, text=text, headers={"greet": "hello"}) ) - res = await q.send_get_request(NormalisedURLPath("/text-api"), {}) + res = await q.send_get_request(NormalisedURLPath("/text-api"), None, None) assert res == { "_text": "foo", "_headers": { @@ -181,7 +181,7 @@ async def test_querier_text_and_headers(): httpx.Response(200, json=body, headers={"greet": "hi"}) ) - res = await q.send_get_request(NormalisedURLPath("/json-api"), {}) + res = await q.send_get_request(NormalisedURLPath("/json-api"), None, None) assert res == { "bar": "baz", "_headers": { diff --git a/tests/test_session.py b/tests/test_session.py index 9d9ed9aa3..b74ed3945 100644 --- a/tests/test_session.py +++ b/tests/test_session.py @@ -95,7 +95,7 @@ async def test_that_once_the_info_is_loaded_it_doesnt_query_again(): raise Exception("Should never come here") response = await create_new_session( - s.recipe_implementation, "public", "", False, {}, {} + s.recipe_implementation, "public", "", False, {}, {}, None ) assert response.session is not None @@ -106,7 +106,7 @@ async def test_that_once_the_info_is_loaded_it_doesnt_query_again(): access_token = parse_jwt_without_signature_verification(response.accessToken.token) await get_session( - s.recipe_implementation, access_token, response.antiCsrfToken, True, False + s.recipe_implementation, access_token, response.antiCsrfToken, True, False, None ) assert ( AllowedProcessStates.CALLING_SERVICE_IN_VERIFY @@ -118,6 +118,7 @@ async def test_that_once_the_info_is_loaded_it_doesnt_query_again(): response.refreshToken.token, response.antiCsrfToken, False, + None, ) assert response2.session is not None @@ -135,6 +136,7 @@ async def test_that_once_the_info_is_loaded_it_doesnt_query_again(): response2.antiCsrfToken, True, False, + None, ) assert ( @@ -157,6 +159,7 @@ async def test_that_once_the_info_is_loaded_it_doesnt_query_again(): response2.antiCsrfToken, True, False, + None, ) assert ( AllowedProcessStates.CALLING_SERVICE_IN_VERIFY @@ -166,7 +169,9 @@ async def test_that_once_the_info_is_loaded_it_doesnt_query_again(): assert response4.session is not None assert response4.accessToken is None - response5 = await revoke_session(s.recipe_implementation, response4.session.handle) + response5 = await revoke_session( + s.recipe_implementation, response4.session.handle, {} + ) assert response5 is True @@ -199,6 +204,7 @@ async def test_creating_many_sessions_for_one_user_and_looping(): False, {"someKey": "someValue"}, {}, + None, ) access_tokens.append(new_session.accessToken.token) @@ -297,7 +303,7 @@ async def test_signout_api_works_even_if_session_is_deleted_after_creation( user_id = "user_id" response = await create_new_session( - s.recipe_implementation, "public", user_id, False, {}, {} + s.recipe_implementation, "public", user_id, False, {}, {}, None ) session_handle = response.session.handle diff --git a/tests/thirdparty/test_emaildelivery.py b/tests/thirdparty/test_emaildelivery.py index 52a020f41..b5b3fdb36 100644 --- a/tests/thirdparty/test_emaildelivery.py +++ b/tests/thirdparty/test_emaildelivery.py @@ -148,7 +148,7 @@ async def test_email_verify_default_backward_compatibility( assert isinstance(resp, ManuallyCreateOrUpdateUserOkResult) user_id = resp.user.user_id response = await create_new_session( - s.recipe_implementation, "public", user_id, True, {}, {} + s.recipe_implementation, "public", user_id, True, {}, {}, None ) def api_side_effect(request: httpx.Request): @@ -222,7 +222,7 @@ async def test_email_verify_default_backward_compatibility_supress_error( assert isinstance(resp, ManuallyCreateOrUpdateUserOkResult) user_id = resp.user.user_id response = await create_new_session( - s.recipe_implementation, "public", user_id, True, {}, {} + s.recipe_implementation, "public", user_id, True, {}, {}, None ) def api_side_effect(request: httpx.Request): @@ -312,7 +312,7 @@ async def send_email( assert isinstance(resp, ManuallyCreateOrUpdateUserOkResult) user_id = resp.user.user_id response = await create_new_session( - s.recipe_implementation, "public", user_id, True, {}, {} + s.recipe_implementation, "public", user_id, True, {}, {}, None ) resp = email_verify_token_request( @@ -391,7 +391,7 @@ async def send_email( user_id = resp.user.user_id assert isinstance(user_id, str) response = await create_new_session( - s.recipe_implementation, "public", user_id, True, {}, {} + s.recipe_implementation, "public", user_id, True, {}, {}, None ) def api_side_effect(request: httpx.Request): @@ -531,7 +531,7 @@ async def send_email_override( user_id = resp.user.user_id assert isinstance(user_id, str) response = await create_new_session( - s.recipe_implementation, "public", user_id, True, {}, {} + s.recipe_implementation, "public", user_id, True, {}, {}, None ) resp = email_verify_token_request( diff --git a/tests/thirdpartyemailpassword/test_email_delivery.py b/tests/thirdpartyemailpassword/test_email_delivery.py index 6c2d3eb2c..0d95d31f2 100644 --- a/tests/thirdpartyemailpassword/test_email_delivery.py +++ b/tests/thirdpartyemailpassword/test_email_delivery.py @@ -504,7 +504,7 @@ async def test_email_verification_default_backward_compatibility( if not isinstance(s.recipe_implementation, SessionRecipeImplementation): raise Exception("Should never come here") response = await create_new_session( - s.recipe_implementation, "public", user_id, True, {}, {} + s.recipe_implementation, "public", user_id, True, {}, {}, None ) def api_side_effect(request: httpx.Request): @@ -570,7 +570,7 @@ async def test_email_verification_default_backward_compatibility_suppress_error( if not isinstance(s.recipe_implementation, SessionRecipeImplementation): raise Exception("Should never come here") response = await create_new_session( - s.recipe_implementation, "public", user_id, True, {}, {} + s.recipe_implementation, "public", user_id, True, {}, {}, None ) def api_side_effect(request: httpx.Request): @@ -654,7 +654,7 @@ async def send_email( if not isinstance(s.recipe_implementation, SessionRecipeImplementation): raise Exception("Should never come here") response = await create_new_session( - s.recipe_implementation, "public", user_id, False, {}, {} + s.recipe_implementation, "public", user_id, False, {}, {}, None ) res = email_verify_token_request( @@ -725,7 +725,7 @@ async def send_email( if not isinstance(s.recipe_implementation, SessionRecipeImplementation): raise Exception("Should never come here") response = await create_new_session( - s.recipe_implementation, "public", user_id, True, {}, {} + s.recipe_implementation, "public", user_id, True, {}, {}, None ) def api_side_effect(request: httpx.Request): @@ -857,7 +857,7 @@ async def send_email_override( if not isinstance(s.recipe_implementation, SessionRecipeImplementation): raise Exception("Should never come here") response = await create_new_session( - s.recipe_implementation, "public", user_id, True, {}, {} + s.recipe_implementation, "public", user_id, True, {}, {}, None ) resp = email_verify_token_request( @@ -942,7 +942,7 @@ async def send_email( if not isinstance(s.recipe_implementation, SessionRecipeImplementation): raise Exception("Should never come here") response = await create_new_session( - s.recipe_implementation, "public", user_id, True, {}, {} + s.recipe_implementation, "public", user_id, True, {}, {}, None ) res = email_verify_token_request( @@ -1021,7 +1021,7 @@ async def send_email( if not isinstance(s.recipe_implementation, SessionRecipeImplementation): raise Exception("Should never come here") response = await create_new_session( - s.recipe_implementation, "public", user_id, True, {}, {} + s.recipe_implementation, "public", user_id, True, {}, {}, None ) res = email_verify_token_request( diff --git a/tests/thirdpartypasswordless/test_emaildelivery.py b/tests/thirdpartypasswordless/test_emaildelivery.py index 344d9eab7..87a2e5791 100644 --- a/tests/thirdpartypasswordless/test_emaildelivery.py +++ b/tests/thirdpartypasswordless/test_emaildelivery.py @@ -162,7 +162,7 @@ async def test_email_verify_default_backward_compatibility( assert isinstance(resp, ThirdPartyManuallyCreateOrUpdateUserOkResult) user_id = resp.user.user_id response = await create_new_session( - s.recipe_implementation, "public", user_id, True, {}, {} + s.recipe_implementation, "public", user_id, True, {}, {}, None ) def api_side_effect(request: httpx.Request): @@ -261,7 +261,7 @@ async def send_email( assert isinstance(resp, ThirdPartyManuallyCreateOrUpdateUserOkResult) user_id = resp.user.user_id response = await create_new_session( - s.recipe_implementation, "public", user_id, True, {}, {} + s.recipe_implementation, "public", user_id, True, {}, {}, None ) resp = email_verify_token_request( @@ -351,7 +351,7 @@ async def send_email( user_id = resp.user.user_id assert isinstance(user_id, str) response = await create_new_session( - s.recipe_implementation, "public", user_id, True, {}, {} + s.recipe_implementation, "public", user_id, True, {}, {}, None ) def api_side_effect(request: httpx.Request): @@ -502,7 +502,7 @@ async def send_email_override( user_id = resp.user.user_id assert isinstance(user_id, str) response = await create_new_session( - s.recipe_implementation, "public", user_id, True, {}, {} + s.recipe_implementation, "public", user_id, True, {}, {}, None ) resp = email_verify_token_request(