Skip to content

Commit 9f0a406

Browse files
committed
feat: All changes related to passwordless multitenancy
1 parent d294501 commit 9f0a406

File tree

69 files changed

+815
-331
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

69 files changed

+815
-331
lines changed

supertokens_python/ingredients/emaildelivery/types.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,9 @@
2424

2525
class EmailDeliveryInterface(ABC, Generic[_T]):
2626
@abstractmethod
27-
async def send_email(self, template_vars: _T, user_context: Dict[str, Any]) -> None:
27+
async def send_email(
28+
self, template_vars: _T, tenant_id: str, user_context: Dict[str, Any]
29+
) -> None:
2830
pass
2931

3032

supertokens_python/ingredients/smsdelivery/types.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,9 @@
2222

2323
class SMSDeliveryInterface(ABC, Generic[_T]):
2424
@abstractmethod
25-
async def send_sms(self, template_vars: _T, user_context: Dict[str, Any]) -> None:
25+
async def send_sms(
26+
self, template_vars: _T, tenant_id: str, user_context: Dict[str, Any]
27+
) -> None:
2628
pass
2729

2830

supertokens_python/recipe/dashboard/api/userdetails/user_email_verify_token_post.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,8 +59,10 @@ async def handle_email_verify_token_post(
5959

6060
assert isinstance(email_verification_token, CreateEmailVerificationTokenOkResult)
6161

62+
# TODO: Pass tenant id
63+
tenant_id = "pass-tenant-id"
6264
email_verify_link = get_email_verify_link(
63-
api_options.app_info, email_verification_token.token, user_id
65+
api_options.app_info, email_verification_token.token, user_id, tenant_id
6466
)
6567

6668
await send_email(

supertokens_python/recipe/emailpassword/api/implementation.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ async def generate_password_reset_token_post(
113113
password_reset_link=password_reset_link,
114114
)
115115
await api_options.email_delivery.ingredient_interface_impl.send_email(
116-
send_email_input, user_context
116+
send_email_input, tenant_id, user_context
117117
)
118118

119119
return GeneratePasswordResetTokenPostOkResult()

supertokens_python/recipe/emailpassword/asyncio/__init__.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -109,10 +109,12 @@ async def sign_up(
109109

110110

111111
async def send_email(
112-
input_: EmailTemplateVars, user_context: Union[None, Dict[str, Any]] = None
112+
input_: EmailTemplateVars,
113+
tenant_id: str,
114+
user_context: Union[None, Dict[str, Any]] = None,
113115
):
114116
if user_context is None:
115117
user_context = {}
116118
return await EmailPasswordRecipe.get_instance().email_delivery.ingredient_interface_impl.send_email(
117-
input_, user_context
119+
input_, tenant_id, user_context
118120
)

supertokens_python/recipe/emailpassword/emaildelivery/services/backward_compatibility/__init__.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,10 @@ def __init__(
8787
)
8888

8989
async def send_email(
90-
self, template_vars: EmailTemplateVars, user_context: Dict[str, Any]
90+
self,
91+
template_vars: EmailTemplateVars,
92+
tenant_id: str,
93+
user_context: Dict[str, Any],
9194
) -> None:
9295
user = await self.recipe_interface_impl.get_user_by_id(
9396
user_id=template_vars.user.id, user_context=user_context

supertokens_python/recipe/emailpassword/emaildelivery/services/smtp/__init__.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,10 @@ def __init__(
4242
self.service_implementation = oi if override is None else override(oi)
4343

4444
async def send_email(
45-
self, template_vars: EmailTemplateVars, user_context: Dict[str, Any]
45+
self,
46+
template_vars: EmailTemplateVars,
47+
tenant_id: str,
48+
user_context: Dict[str, Any],
4649
) -> None:
4750
content = await self.service_implementation.get_content(
4851
template_vars, user_context

supertokens_python/recipe/emailpassword/syncio/__init__.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -103,8 +103,10 @@ def sign_up(
103103

104104

105105
def send_email(
106-
input_: EmailTemplateVars, user_context: Union[None, Dict[str, Any]] = None
106+
input_: EmailTemplateVars,
107+
tenant_id: str,
108+
user_context: Union[None, Dict[str, Any]] = None,
107109
):
108110
from supertokens_python.recipe.emailpassword.asyncio import send_email
109111

110-
return sync(send_email(input_, user_context))
112+
return sync(send_email(input_, tenant_id, user_context))

supertokens_python/recipe/emailpassword/types.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ def __init__(
3030
self.time_joined = time_joined
3131
self.tenant_ids = tenant_ids
3232

33-
def __eq__(self, other: "User"): # type: ignore
33+
def __eq__(self, other: "User"): # type: ignore
3434
return (
3535
isinstance(other, self.__class__)
3636
and self.user_id == other.user_id

supertokens_python/recipe/emailverification/api/email_verify.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828

2929
async def handle_email_verify_api(
3030
api_implementation: APIInterface,
31+
tenant_id: str,
3132
api_options: APIOptions,
3233
user_context: Dict[str, Any],
3334
):
@@ -52,7 +53,7 @@ async def handle_email_verify_api(
5253
)
5354

5455
result = await api_implementation.email_verify_post(
55-
token, session, api_options, user_context
56+
token, session, tenant_id, api_options, user_context
5657
)
5758
else:
5859
if api_implementation.disable_is_email_verified_get:

supertokens_python/recipe/emailverification/asyncio/__init__.py

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,13 @@
2323
from supertokens_python.recipe.emailverification.types import EmailTemplateVars
2424
from supertokens_python.recipe.emailverification.recipe import EmailVerificationRecipe
2525

26+
from supertokens_python.recipe.multitenancy.constants import DEFAULT_TENANT_ID
27+
2628

2729
async def create_email_verification_token(
2830
user_id: str,
2931
email: Optional[str] = None,
32+
tenant_id: Optional[str] = None,
3033
user_context: Union[None, Dict[str, Any]] = None,
3134
) -> Union[
3235
CreateEmailVerificationTokenOkResult,
@@ -45,17 +48,19 @@ async def create_email_verification_token(
4548
raise Exception("Unknown User ID provided without email")
4649

4750
return await recipe.recipe_implementation.create_email_verification_token(
48-
user_id, email, user_context
51+
user_id, email, tenant_id or DEFAULT_TENANT_ID, user_context
4952
)
5053

5154

5255
async def verify_email_using_token(
53-
token: str, user_context: Union[None, Dict[str, Any]] = None
56+
token: str,
57+
tenant_id: Optional[str] = None,
58+
user_context: Union[None, Dict[str, Any]] = None,
5459
):
5560
if user_context is None:
5661
user_context = {}
5762
return await EmailVerificationRecipe.get_instance().recipe_implementation.verify_email_using_token(
58-
token, user_context
63+
token, tenant_id or DEFAULT_TENANT_ID, user_context
5964
)
6065

6166

@@ -85,6 +90,7 @@ async def is_email_verified(
8590
async def revoke_email_verification_tokens(
8691
user_id: str,
8792
email: Optional[str] = None,
93+
tenant_id: Optional[str] = None,
8894
user_context: Optional[Dict[str, Any]] = None,
8995
) -> RevokeEmailVerificationTokensOkResult:
9096
if user_context is None:
@@ -101,7 +107,7 @@ async def revoke_email_verification_tokens(
101107
raise Exception("Unknown User ID provided without email")
102108

103109
return await EmailVerificationRecipe.get_instance().recipe_implementation.revoke_email_verification_tokens(
104-
user_id, email, user_context
110+
user_id, email, tenant_id or DEFAULT_TENANT_ID, user_context
105111
)
106112

107113

@@ -131,10 +137,12 @@ async def unverify_email(
131137

132138

133139
async def send_email(
134-
input_: EmailTemplateVars, user_context: Union[None, Dict[str, Any]] = None
140+
input_: EmailTemplateVars,
141+
tenant_id: Optional[str] = None,
142+
user_context: Union[None, Dict[str, Any]] = None,
135143
):
136144
if user_context is None:
137145
user_context = {}
138146
return await EmailVerificationRecipe.get_instance().email_delivery.ingredient_interface_impl.send_email(
139-
input_, user_context
147+
input_, tenant_id or DEFAULT_TENANT_ID, user_context
140148
)

supertokens_python/recipe/emailverification/emaildelivery/services/backward_compatibility/__init__.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,10 @@ def __init__(
6666
)
6767

6868
async def send_email(
69-
self, template_vars: VerificationEmailTemplateVars, user_context: Dict[str, Any]
69+
self,
70+
template_vars: VerificationEmailTemplateVars,
71+
tenant_id: str,
72+
user_context: Dict[str, Any],
7073
) -> None:
7174
try:
7275
email_user = User(template_vars.user.id, template_vars.user.email)

supertokens_python/recipe/emailverification/emaildelivery/services/smtp/__init__.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,10 @@ def __init__(
4141
self.service_implementation = oi if override is None else override(oi)
4242

4343
async def send_email(
44-
self, template_vars: VerificationEmailTemplateVars, user_context: Dict[str, Any]
44+
self,
45+
template_vars: VerificationEmailTemplateVars,
46+
tenant_id: str,
47+
user_context: Dict[str, Any],
4548
) -> None:
4649
content = await self.service_implementation.get_content(
4750
template_vars, user_context

supertokens_python/recipe/emailverification/interfaces.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ def __init__(self):
6161

6262
@abstractmethod
6363
async def create_email_verification_token(
64-
self, user_id: str, email: str, user_context: Dict[str, Any]
64+
self, user_id: str, email: str, tenant_id: str, user_context: Dict[str, Any]
6565
) -> Union[
6666
CreateEmailVerificationTokenOkResult,
6767
CreateEmailVerificationTokenEmailAlreadyVerifiedError,
@@ -70,7 +70,7 @@ async def create_email_verification_token(
7070

7171
@abstractmethod
7272
async def verify_email_using_token(
73-
self, token: str, user_context: Dict[str, Any]
73+
self, token: str, tenant_id: str, user_context: Dict[str, Any]
7474
) -> Union[VerifyEmailUsingTokenOkResult, VerifyEmailUsingTokenInvalidTokenError]:
7575
pass
7676

@@ -82,7 +82,7 @@ async def is_email_verified(
8282

8383
@abstractmethod
8484
async def revoke_email_verification_tokens(
85-
self, user_id: str, email: str, user_context: Dict[str, Any]
85+
self, user_id: str, email: str, tenant_id: str, user_context: Dict[str, Any]
8686
) -> RevokeEmailVerificationTokensOkResult:
8787
pass
8888

@@ -169,6 +169,7 @@ async def email_verify_post(
169169
self,
170170
token: str,
171171
session: Optional[SessionContainer],
172+
tenant_id: str,
172173
api_options: APIOptions,
173174
user_context: Dict[str, Any],
174175
) -> Union[

supertokens_python/recipe/emailverification/recipe.py

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,7 @@ def get_apis_handled(self) -> List[APIHandled]:
165165
async def handle_api_request(
166166
self,
167167
request_id: str,
168-
tenant_id: Optional[str],
168+
tenant_id: str,
169169
request: BaseRequest,
170170
path: NormalisedURLPath,
171171
method: str,
@@ -186,7 +186,7 @@ async def handle_api_request(
186186
self.api_implementation, api_options, user_context
187187
)
188188
return await handle_email_verify_api(
189-
self.api_implementation, api_options, user_context
189+
self.api_implementation, tenant_id, api_options, user_context
190190
)
191191

192192
async def handle_error(
@@ -342,12 +342,13 @@ async def email_verify_post(
342342
self,
343343
token: str,
344344
session: Optional[SessionContainer],
345+
tenant_id: str,
345346
api_options: APIOptions,
346347
user_context: Dict[str, Any],
347348
) -> Union[EmailVerifyPostOkResult, EmailVerifyPostInvalidTokenError]:
348349

349350
response = await api_options.recipe_implementation.verify_email_using_token(
350-
token, user_context
351+
token, tenant_id, user_context
351352
)
352353
if isinstance(response, VerifyEmailUsingTokenOkResult):
353354
if session is not None:
@@ -411,6 +412,7 @@ async def generate_email_verify_token_post(
411412
email_info = await EmailVerificationRecipe.get_instance().get_email_for_user_id(
412413
user_id, user_context
413414
)
415+
tenant_id = session.get_access_token_payload()["tid"]
414416

415417
if isinstance(email_info, EmailDoesNotExistError):
416418
log_debug_message(
@@ -423,6 +425,7 @@ async def generate_email_verify_token_post(
423425
await api_options.recipe_implementation.create_email_verification_token(
424426
user_id,
425427
email_info.email,
428+
tenant_id,
426429
user_context,
427430
)
428431
)
@@ -450,7 +453,7 @@ async def generate_email_verify_token_post(
450453
await session.fetch_and_set_claim(EmailVerificationClaim, user_context)
451454

452455
email_verify_link = get_email_verify_link(
453-
api_options.app_info, response.token, api_options.recipe_id
456+
api_options.app_info, response.token, api_options.recipe_id, tenant_id
454457
)
455458

456459
log_debug_message("Sending email verification email to %s", email_info)
@@ -460,7 +463,7 @@ async def generate_email_verify_token_post(
460463
user_context=user_context,
461464
)
462465
await api_options.email_delivery.ingredient_interface_impl.send_email(
463-
email_verification_email_delivery_input, user_context
466+
email_verification_email_delivery_input, tenant_id, user_context
464467
)
465468
return GenerateEmailVerifyTokenPostOkResult()
466469

supertokens_python/recipe/emailverification/recipe_implementation.py

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -41,25 +41,25 @@ def __init__(self, querier: Querier, config: EmailVerificationConfig):
4141
self.config = config
4242

4343
async def create_email_verification_token(
44-
self, user_id: str, email: str, user_context: Dict[str, Any]
44+
self, user_id: str, email: str, tenant_id: str, user_context: Dict[str, Any]
4545
) -> Union[
4646
CreateEmailVerificationTokenOkResult,
4747
CreateEmailVerificationTokenEmailAlreadyVerifiedError,
4848
]:
4949
data = {"userId": user_id, "email": email}
5050
response = await self.querier.send_post_request(
51-
NormalisedURLPath("/recipe/user/email/verify/token"), data
51+
NormalisedURLPath(f"{tenant_id}/recipe/user/email/verify/token"), data
5252
)
5353
if "status" in response and response["status"] == "OK":
5454
return CreateEmailVerificationTokenOkResult(response["token"])
5555
return CreateEmailVerificationTokenEmailAlreadyVerifiedError()
5656

5757
async def verify_email_using_token(
58-
self, token: str, user_context: Dict[str, Any]
58+
self, token: str, tenant_id: str, user_context: Dict[str, Any]
5959
) -> Union[VerifyEmailUsingTokenOkResult, VerifyEmailUsingTokenInvalidTokenError]:
6060
data = {"method": "token", "token": token}
6161
response = await self.querier.send_post_request(
62-
NormalisedURLPath("/recipe/user/email/verify"), data
62+
NormalisedURLPath(f"{tenant_id}/recipe/user/email/verify"), data
6363
)
6464
if "status" in response and response["status"] == "OK":
6565
return VerifyEmailUsingTokenOkResult(
@@ -77,11 +77,12 @@ async def is_email_verified(
7777
return response["isVerified"]
7878

7979
async def revoke_email_verification_tokens(
80-
self, user_id: str, email: str, user_context: Dict[str, Any]
80+
self, user_id: str, email: str, tenant_id: str, user_context: Dict[str, Any]
8181
) -> RevokeEmailVerificationTokensOkResult:
8282
data = {"userId": user_id, "email": email}
8383
await self.querier.send_post_request(
84-
NormalisedURLPath("/recipe/user/email/verify/token/remove"), data
84+
NormalisedURLPath(f"{tenant_id}/recipe/user/email/verify/token/remove"),
85+
data,
8586
)
8687
return RevokeEmailVerificationTokensOkResult()
8788

0 commit comments

Comments
 (0)