Skip to content

Commit

Permalink
Merge pull request #860 from supertokens/python-update
Browse files Browse the repository at this point in the history
Python update for account linking
  • Loading branch information
rishabhpoddar authored Oct 25, 2024
2 parents e8f1c3f + dbf604a commit 4e9e52d
Show file tree
Hide file tree
Showing 187 changed files with 9,237 additions and 3,496 deletions.
23 changes: 13 additions & 10 deletions v2/attackprotectionsuite/backend-setup.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -1038,13 +1038,14 @@ from supertokens_python.recipe.emailpassword.interfaces import APIInterface, API
from supertokens_python.recipe.emailpassword.types import FormField
from supertokens_python.framework import BaseRequest
from supertokens_python.types import GeneralErrorResponse
from supertokens_python.recipe.session import SessionContainer

SECRET_API_KEY = "<secret-api-key>"; # Your secret API key that you received from the SuperTokens team
# The full URL with the correct region will be provided by the SuperTokens team
ANOMALY_DETECTION_API_URL = "https://security-<region>.aws.supertokens.io/v1/security"

async def handle_security_checks(request_id: Union[str, None], password: Union[str, None], brute_force_config: Union[List[Dict[str, Any]], None], email: Union[str, None], phone_number: Union[str, None], action_type: Union[str, None]) -> Union[GeneralErrorResponse, None]:
request_body = {}
request_body: Dict[str, Any] = {}

if request_id is not None:
request_body['requestId'] = request_id
Expand Down Expand Up @@ -1117,7 +1118,8 @@ def get_brute_force_config(user_identifier: Union[str, None], ip: str, prefix: U
# highlight-start
def override_email_password_apis(original_implementation: APIInterface):
original_sign_up_post = original_implementation.sign_up_post
async def sign_up_post(form_fields: List[FormField], tenant_id: str, api_options: APIOptions, user_context: Dict[str, Any]):
async def sign_up_post(form_fields: List[FormField], tenant_id: str, session: Union[SessionContainer, None],
should_try_linking_with_session_user: Union[bool, None], api_options: APIOptions, user_context: Dict[str, Any]):
request_body = await api_options.request.json()
if not request_body:
return GeneralErrorResponse(message="The request body is required")
Expand Down Expand Up @@ -1150,13 +1152,13 @@ def override_email_password_apis(original_implementation: APIInterface):
return security_check_response

# We need to call the original implementation of sign_up_post.
response = await original_sign_up_post(form_fields, tenant_id, api_options, user_context)
response = await original_sign_up_post(form_fields, tenant_id, session, should_try_linking_with_session_user, api_options, user_context)

return response
original_implementation.sign_up_post = sign_up_post

original_sign_in_post = original_implementation.sign_in_post
async def sign_in_post(form_fields: List[FormField], tenant_id: str, api_options: APIOptions, user_context: Dict[str, Any]):
async def sign_in_post(form_fields: List[FormField], tenant_id: str, session: Union[SessionContainer, None], should_try_linking_with_session_user: Union[bool, None], api_options: APIOptions, user_context: Dict[str, Any]):
request_body = await api_options.request.json()
if not request_body:
return GeneralErrorResponse(message="The request body is required")
Expand Down Expand Up @@ -1186,7 +1188,7 @@ def override_email_password_apis(original_implementation: APIInterface):
return security_check_response

# We need to call the original implementation of sign_in_post.
response = await original_sign_in_post(form_fields, tenant_id, api_options, user_context)
response = await original_sign_in_post(form_fields, tenant_id, session, should_try_linking_with_session_user, api_options, user_context)

return response
original_implementation.sign_in_post = sign_in_post
Expand Down Expand Up @@ -1710,13 +1712,14 @@ from supertokens_python.recipe.passwordless.interfaces import APIInterface, APIO
from supertokens_python.recipe.passwordless.asyncio import list_codes_by_device_id
from supertokens_python.framework import BaseRequest
from supertokens_python.types import GeneralErrorResponse
from supertokens_python.recipe.session import SessionContainer

SECRET_API_KEY = "<secret-api-key>" # Your secret API key that you received from the SuperTokens team
# The full URL with the correct region will be provided by the SuperTokens team
ANOMALY_DETECTION_API_URL = "https://security-<region>.aws.supertokens.io/v1/security"

async def handle_security_checks(request_id: Union[str, None], password: Union[str, None], brute_force_config: Union[List[Dict[str, Any]], None], email: Union[str, None], phone_number: Union[str, None], action_type: Union[str, None]) -> Union[GeneralErrorResponse, None]:
request_body = {}
request_body: Dict[str, Any] = {}

request_body['bruteForce'] = brute_force_config
request_body['email'] = email
Expand Down Expand Up @@ -1767,7 +1770,7 @@ def get_brute_force_config(user_identifier: Union[str, None], ip: str, prefix: U
# highlight-start
def override_passwordless_apis(original_implementation: APIInterface):
original_create_code_post = original_implementation.create_code_post
async def create_code_post(email: Union[str, None], phone_number: Union[str, None], tenant_id: str, api_options: APIOptions, user_context: Dict[str, Any]):
async def create_code_post(email: Union[str, None], phone_number: Union[str, None], session: Union[SessionContainer, None], should_try_linking_with_session_user: Union[bool, None], tenant_id: str, api_options: APIOptions, user_context: Dict[str, Any]):
action_type = 'passwordless-send-sms'
ip = get_ip_from_request(api_options.request)
identifier = None
Expand All @@ -1790,13 +1793,13 @@ def override_passwordless_apis(original_implementation: APIInterface):
return security_check_response

# We need to call the original implementation of create_code_post.
response = await original_create_code_post(email, phone_number, tenant_id, api_options, user_context)
response = await original_create_code_post(email, phone_number, session, should_try_linking_with_session_user,tenant_id, api_options, user_context)

return response
original_implementation.create_code_post = create_code_post

original_resend_code_post = original_implementation.resend_code_post
async def resend_code_post(device_id: str, pre_auth_session_id: str, tenant_id: str, api_options: APIOptions, user_context: Dict[str, Any]):
async def resend_code_post(device_id: str, pre_auth_session_id: str, session: Union[SessionContainer, None], should_try_linking_with_session_user: Union[bool, None], tenant_id: str, api_options: APIOptions, user_context: Dict[str, Any]):
action_type = 'passwordless-send-sms'
ip = get_ip_from_request(api_options.request)
email = None
Expand Down Expand Up @@ -1825,7 +1828,7 @@ def override_passwordless_apis(original_implementation: APIInterface):
return security_check_response

# We need to call the original implementation of resend_code_post.
response = await original_resend_code_post(device_id, pre_auth_session_id, tenant_id, api_options, user_context)
response = await original_resend_code_post(device_id, pre_auth_session_id, session, should_try_linking_with_session_user, tenant_id, api_options, user_context)

return response
original_implementation.resend_code_post = resend_code_post
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -120,30 +120,57 @@ func emailNotAllowed(email string) bool {

```python
from supertokens_python.recipe import emailpassword
from supertokens_python.recipe.emailpassword.interfaces import APIInterface, SignUpPostOkResult, SignUpPostEmailAlreadyExistsError, APIOptions
from supertokens_python.recipe.emailpassword.interfaces import (
APIInterface,
SignUpPostOkResult,
EmailAlreadyExistsError,
SignUpPostNotAllowedResponse,
APIOptions,
)
from supertokens_python.recipe.emailpassword.types import FormField
from typing import Any, Dict, Union, List
from supertokens_python.types import GeneralErrorResponse
from supertokens_python.recipe.session import SessionContainer


def override_apis(original_implementation: APIInterface):

# first we copy the original implementation
original_sign_up = original_implementation.sign_up_post

async def sign_up(form_fields: List[FormField], tenant_id: str,
api_options: APIOptions, user_context: Dict[str, Any]) -> Union[SignUpPostOkResult, SignUpPostEmailAlreadyExistsError, GeneralErrorResponse]:
async def sign_up(
form_fields: List[FormField],
tenant_id: str,
session: Union[SessionContainer, None],
should_try_linking_with_session_user: Union[bool, None],
api_options: APIOptions,
user_context: Dict[str, Any],
) -> Union[
SignUpPostOkResult,
EmailAlreadyExistsError,
SignUpPostNotAllowedResponse,
GeneralErrorResponse,
]:
email = ""
for i in range(len(form_fields)):
if form_fields[i].id == "email":
email = form_fields[i].value

if (is_not_allowed(email)):
if is_not_allowed(email):
# highlight-start
return GeneralErrorResponse(message="You are not allowed to sign up. Please contact the app's admin to get permission")
return GeneralErrorResponse(
message="You are not allowed to sign up. Please contact the app's admin to get permission"
)
# highlight-end

return await original_sign_up(form_fields, tenant_id, api_options, user_context)
return await original_sign_up(
form_fields,
tenant_id,
session,
should_try_linking_with_session_user,
api_options,
user_context,
)

original_implementation.sign_up_post = sign_up

Expand All @@ -155,11 +182,7 @@ def is_not_allowed(email: str):
return True


emailpassword.init(
override=emailpassword.InputOverrideConfig(
apis=override_apis
)
)
emailpassword.init(override=emailpassword.InputOverrideConfig(apis=override_apis))
```

</TabItem>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,19 +107,23 @@ from supertokens_python.recipe.session.asyncio import get_all_session_handles_fo
from supertokens_python.recipe import session
from supertokens_python.recipe.session.interfaces import RecipeInterface
from typing import Any, Dict, Optional
from supertokens_python.types import RecipeUserId


def override_session_functions(original_implementation: RecipeInterface):

# first we copy the original implementation
original_create_new_session = original_implementation.create_new_session

async def create_new_session(user_id: str,
access_token_payload: Optional[Dict[str, Any]],
session_data_in_database: Optional[Dict[str, Any]],
disable_anti_csrf: Optional[bool],
tenant_id: str,
user_context: Dict[str, Any]):
async def create_new_session(
user_id: str,
recipe_user_id: RecipeUserId,
access_token_payload: Optional[Dict[str, Any]],
session_data_in_database: Optional[Dict[str, Any]],
disable_anti_csrf: Optional[bool],
tenant_id: str,
user_context: Dict[str, Any],
):
# highlight-start
existing_sessions = await get_all_session_handles_for_user(user_id)

Expand All @@ -128,19 +132,23 @@ def override_session_functions(original_implementation: RecipeInterface):
raise Exception("Session already exists on another device")

# no other session exists, and so we can continue with logging in this user
return await original_create_new_session(user_id, access_token_payload, session_data_in_database, disable_anti_csrf, tenant_id, user_context)
return await original_create_new_session(
user_id,
recipe_user_id,
access_token_payload,
session_data_in_database,
disable_anti_csrf,
tenant_id,
user_context,
)
# highlight-end

original_implementation.create_new_session = create_new_session

return original_implementation


session.init(
override=session.InputOverrideConfig(
functions=override_session_functions
)
)
session.init(override=session.InputOverrideConfig(functions=override_session_functions))
```

</TabItem>
Expand Down
37 changes: 26 additions & 11 deletions v2/emailpassword/advanced-customizations/apis-override/usage.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -125,32 +125,47 @@ See all the [functions that can be overrided here](https://supertokens.com/docs/
```python
from supertokens_python import init, InputAppInfo
from supertokens_python.recipe import emailpassword
from supertokens_python.recipe.emailpassword.interfaces import APIInterface as EmailPasswordAPIInterface, APIOptions as EPAPIOptions
from supertokens_python.recipe.emailpassword.interfaces import (
APIInterface as EmailPasswordAPIInterface,
APIOptions,
)
from supertokens_python.recipe.emailpassword.types import FormField
from typing import List, Dict, Any
from typing import List, Dict, Any, Union
from supertokens_python.recipe.session import SessionContainer


# highlight-start
def override_email_password_apis(original_implementation: EmailPasswordAPIInterface):
original_sign_up_post = original_implementation.sign_up_post

async def sign_up_post(form_fields: List[FormField], tenant_id: str,
api_options: EPAPIOptions,
user_context: Dict[str, Any]):
async def sign_up_post(
form_fields: List[FormField],
tenant_id: str,
session: Union[SessionContainer, None],
should_try_linking_with_session_user: Union[bool, None],
api_options: APIOptions,
user_context: Dict[str, Any],
):
# TODO: custom logic

# or call the default behaviour as show below
return await original_sign_up_post(form_fields, tenant_id, api_options, user_context)
return await original_sign_up_post(
form_fields,
tenant_id,
session,
should_try_linking_with_session_user,
api_options,
user_context,
)

original_implementation.sign_up_post = sign_up_post
return original_implementation
# highlight-end


init(
app_info=InputAppInfo(
api_domain="...", app_name="...", website_domain="..."),

framework='...', # type: ignore
app_info=InputAppInfo(api_domain="...", app_name="...", website_domain="..."),
framework="...", # type: ignore
recipe_list=[
emailpassword.init(
# highlight-start
Expand All @@ -159,7 +174,7 @@ init(
)
# highlight-end
)
]
],
)
```

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -120,27 +120,45 @@ See all the [functions that can be overrided here](https://supertokens.com/docs/
```python
from supertokens_python import init, InputAppInfo
from supertokens_python.recipe import emailpassword
from supertokens_python.recipe.emailpassword.interfaces import RecipeInterface as EPInterface
from typing import Dict, Any
from supertokens_python.recipe.emailpassword.interfaces import (
RecipeInterface as EPInterface,
)
from typing import Dict, Any, Union
from supertokens_python.recipe.session import SessionContainer


# highlight-start
def override_email_password_functions(original_implementation: EPInterface):
original_sign_up = original_implementation.sign_up

async def sign_up(email: str, password: str, tenant_id: str, user_context: Dict[str, Any]):
# TODO: custom logic
original_sign_up = original_implementation.sign_up

async def sign_up(
email: str,
password: str,
tenant_id: str,
session: Union[SessionContainer, None],
should_try_linking_with_session_user: Union[bool, None],
user_context: Dict[str, Any],
):
# TODO: custom logic

# or call the default behaviour as show below
return await original_sign_up(
email,
password,
tenant_id,
session,
should_try_linking_with_session_user,
user_context,
)

# or call the default behaviour as show below
return await original_sign_up(email, password, tenant_id, user_context)

original_implementation.sign_up = sign_up
return original_implementation
original_implementation.sign_up = sign_up
return original_implementation
# highlight-end


init(
app_info=InputAppInfo(api_domain="...", app_name="...", website_domain="..."),
framework='...', # type: ignore
framework="...", # type: ignore
recipe_list=[
emailpassword.init(
# highlight-start
Expand All @@ -149,7 +167,7 @@ init(
)
# highlight-end
)
]
],
)
```

Expand Down
Loading

0 comments on commit 4e9e52d

Please sign in to comment.