Skip to content

feat: All changes related to passwordless multitenancy #380

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Aug 3, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -59,15 +59,17 @@ async def handle_email_verify_token_post(

assert isinstance(email_verification_token, CreateEmailVerificationTokenOkResult)

# TODO: Pass tenant id
tenant_id = "pass-tenant-id"
email_verify_link = get_email_verify_link(
api_options.app_info, email_verification_token.token, user_id
api_options.app_info, email_verification_token.token, user_id, tenant_id
)

await send_email(
VerificationEmailTemplateVars(
user=VerificationEmailTemplateVarsUser(user_id, email_response.email),
email_verify_link=email_verify_link,
user_context={},
tenant_id=tenant_id,
)
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ async def generate_password_reset_token_post(
send_email_input = PasswordResetEmailTemplateVars(
user=PasswordResetEmailTemplateVarsUser(user.user_id, user.email),
password_reset_link=password_reset_link,
tenant_id=tenant_id,
)
await api_options.email_delivery.ingredient_interface_impl.send_email(
send_email_input, user_context
Expand Down
3 changes: 2 additions & 1 deletion supertokens_python/recipe/emailpassword/asyncio/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,8 @@ async def sign_up(


async def send_email(
input_: EmailTemplateVars, user_context: Union[None, Dict[str, Any]] = None
input_: EmailTemplateVars,
user_context: Union[None, Dict[str, Any]] = None,
):
if user_context is None:
user_context = {}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,9 @@ def __init__(
)

async def send_email(
self, template_vars: EmailTemplateVars, user_context: Dict[str, Any]
self,
template_vars: EmailTemplateVars,
user_context: Dict[str, Any],
) -> None:
user = await self.recipe_interface_impl.get_user_by_id(
user_id=template_vars.user.id, user_context=user_context
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,9 @@ def __init__(
self.service_implementation = oi if override is None else override(oi)

async def send_email(
self, template_vars: EmailTemplateVars, user_context: Dict[str, Any]
self,
template_vars: EmailTemplateVars,
user_context: Dict[str, Any],
) -> None:
content = await self.service_implementation.get_content(
template_vars, user_context
Expand Down
3 changes: 2 additions & 1 deletion supertokens_python/recipe/emailpassword/syncio/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,8 @@ def sign_up(


def send_email(
input_: EmailTemplateVars, user_context: Union[None, Dict[str, Any]] = None
input_: EmailTemplateVars,
user_context: Union[None, Dict[str, Any]] = None,
):
from supertokens_python.recipe.emailpassword.asyncio import send_email

Expand Down
6 changes: 4 additions & 2 deletions supertokens_python/recipe/emailpassword/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
# License for the specific language governing permissions and limitations
# under the License.
from __future__ import annotations
from typing import Awaitable, Callable, List, TypeVar, Union
from typing import Awaitable, Callable, List, TypeVar, Union, Optional

from supertokens_python.ingredients.emaildelivery import EmailDeliveryIngredient
from supertokens_python.ingredients.emaildelivery.types import (
Expand All @@ -30,7 +30,7 @@ def __init__(
self.time_joined = time_joined
self.tenant_ids = tenant_ids

def __eq__(self, other: "User"): # type: ignore
def __eq__(self, other: object):
return (
isinstance(other, self.__class__)
and self.user_id == other.user_id
Expand Down Expand Up @@ -99,9 +99,11 @@ def __init__(
self,
user: PasswordResetEmailTemplateVarsUser,
password_reset_link: str,
tenant_id: Optional[str],
) -> None:
self.user = user
self.password_reset_link = password_reset_link
self.tenant_id = tenant_id


# Export:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@

async def handle_email_verify_api(
api_implementation: APIInterface,
tenant_id: str,
api_options: APIOptions,
user_context: Dict[str, Any],
):
Expand All @@ -52,7 +53,7 @@ async def handle_email_verify_api(
)

result = await api_implementation.email_verify_post(
token, session, api_options, user_context
token, session, tenant_id, api_options, user_context
)
else:
if api_implementation.disable_is_email_verified_get:
Expand Down
17 changes: 12 additions & 5 deletions supertokens_python/recipe/emailverification/asyncio/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,13 @@
from supertokens_python.recipe.emailverification.types import EmailTemplateVars
from supertokens_python.recipe.emailverification.recipe import EmailVerificationRecipe

from supertokens_python.recipe.multitenancy.constants import DEFAULT_TENANT_ID


async def create_email_verification_token(
user_id: str,
email: Optional[str] = None,
tenant_id: Optional[str] = None,
user_context: Union[None, Dict[str, Any]] = None,
) -> Union[
CreateEmailVerificationTokenOkResult,
Expand All @@ -45,17 +48,19 @@ async def create_email_verification_token(
raise Exception("Unknown User ID provided without email")

return await recipe.recipe_implementation.create_email_verification_token(
user_id, email, user_context
user_id, email, tenant_id or DEFAULT_TENANT_ID, user_context
)


async def verify_email_using_token(
token: str, user_context: Union[None, Dict[str, Any]] = None
token: str,
tenant_id: Optional[str] = None,
user_context: Union[None, Dict[str, Any]] = None,
):
if user_context is None:
user_context = {}
return await EmailVerificationRecipe.get_instance().recipe_implementation.verify_email_using_token(
token, user_context
token, tenant_id or DEFAULT_TENANT_ID, user_context
)


Expand Down Expand Up @@ -85,6 +90,7 @@ async def is_email_verified(
async def revoke_email_verification_tokens(
user_id: str,
email: Optional[str] = None,
tenant_id: Optional[str] = None,
user_context: Optional[Dict[str, Any]] = None,
) -> RevokeEmailVerificationTokensOkResult:
if user_context is None:
Expand All @@ -101,7 +107,7 @@ async def revoke_email_verification_tokens(
raise Exception("Unknown User ID provided without email")

return await EmailVerificationRecipe.get_instance().recipe_implementation.revoke_email_verification_tokens(
user_id, email, user_context
user_id, email, tenant_id or DEFAULT_TENANT_ID, user_context
)


Expand Down Expand Up @@ -131,7 +137,8 @@ async def unverify_email(


async def send_email(
input_: EmailTemplateVars, user_context: Union[None, Dict[str, Any]] = None
input_: EmailTemplateVars,
user_context: Union[None, Dict[str, Any]] = None,
):
if user_context is None:
user_context = {}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,9 @@ def __init__(
)

async def send_email(
self, template_vars: VerificationEmailTemplateVars, user_context: Dict[str, Any]
self,
template_vars: VerificationEmailTemplateVars,
user_context: Dict[str, Any],
) -> None:
try:
email_user = User(template_vars.user.id, template_vars.user.email)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,9 @@ def __init__(
self.service_implementation = oi if override is None else override(oi)

async def send_email(
self, template_vars: VerificationEmailTemplateVars, user_context: Dict[str, Any]
self,
template_vars: VerificationEmailTemplateVars,
user_context: Dict[str, Any],
) -> None:
content = await self.service_implementation.get_content(
template_vars, user_context
Expand Down
7 changes: 4 additions & 3 deletions supertokens_python/recipe/emailverification/interfaces.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ def __init__(self):

@abstractmethod
async def create_email_verification_token(
self, user_id: str, email: str, user_context: Dict[str, Any]
self, user_id: str, email: str, tenant_id: str, user_context: Dict[str, Any]
) -> Union[
CreateEmailVerificationTokenOkResult,
CreateEmailVerificationTokenEmailAlreadyVerifiedError,
Expand All @@ -70,7 +70,7 @@ async def create_email_verification_token(

@abstractmethod
async def verify_email_using_token(
self, token: str, user_context: Dict[str, Any]
self, token: str, tenant_id: str, user_context: Dict[str, Any]
) -> Union[VerifyEmailUsingTokenOkResult, VerifyEmailUsingTokenInvalidTokenError]:
pass

Expand All @@ -82,7 +82,7 @@ async def is_email_verified(

@abstractmethod
async def revoke_email_verification_tokens(
self, user_id: str, email: str, user_context: Dict[str, Any]
self, user_id: str, email: str, tenant_id: str, user_context: Dict[str, Any]
) -> RevokeEmailVerificationTokensOkResult:
pass

Expand Down Expand Up @@ -169,6 +169,7 @@ async def email_verify_post(
self,
token: str,
session: Optional[SessionContainer],
tenant_id: str,
api_options: APIOptions,
user_context: Dict[str, Any],
) -> Union[
Expand Down
13 changes: 8 additions & 5 deletions supertokens_python/recipe/emailverification/recipe.py
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ def get_apis_handled(self) -> List[APIHandled]:
async def handle_api_request(
self,
request_id: str,
tenant_id: Optional[str],
tenant_id: str,
request: BaseRequest,
path: NormalisedURLPath,
method: str,
Expand All @@ -186,7 +186,7 @@ async def handle_api_request(
self.api_implementation, api_options, user_context
)
return await handle_email_verify_api(
self.api_implementation, api_options, user_context
self.api_implementation, tenant_id, api_options, user_context
)

async def handle_error(
Expand Down Expand Up @@ -342,12 +342,13 @@ async def email_verify_post(
self,
token: str,
session: Optional[SessionContainer],
tenant_id: str,
api_options: APIOptions,
user_context: Dict[str, Any],
) -> Union[EmailVerifyPostOkResult, EmailVerifyPostInvalidTokenError]:

response = await api_options.recipe_implementation.verify_email_using_token(
token, user_context
token, tenant_id, user_context
)
if isinstance(response, VerifyEmailUsingTokenOkResult):
if session is not None:
Expand Down Expand Up @@ -411,6 +412,7 @@ async def generate_email_verify_token_post(
email_info = await EmailVerificationRecipe.get_instance().get_email_for_user_id(
user_id, user_context
)
tenant_id = session.get_access_token_payload()["tId"]

if isinstance(email_info, EmailDoesNotExistError):
log_debug_message(
Expand All @@ -423,6 +425,7 @@ async def generate_email_verify_token_post(
await api_options.recipe_implementation.create_email_verification_token(
user_id,
email_info.email,
tenant_id,
user_context,
)
)
Expand Down Expand Up @@ -450,14 +453,14 @@ async def generate_email_verify_token_post(
await session.fetch_and_set_claim(EmailVerificationClaim, user_context)

email_verify_link = get_email_verify_link(
api_options.app_info, response.token, api_options.recipe_id
api_options.app_info, response.token, api_options.recipe_id, tenant_id
)

log_debug_message("Sending email verification email to %s", email_info)
email_verification_email_delivery_input = VerificationEmailTemplateVars(
user=VerificationEmailTemplateVarsUser(user_id, email_info.email),
email_verify_link=email_verify_link,
user_context=user_context,
tenant_id=tenant_id,
)
await api_options.email_delivery.ingredient_interface_impl.send_email(
email_verification_email_delivery_input, user_context
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,25 +41,25 @@ def __init__(self, querier: Querier, config: EmailVerificationConfig):
self.config = config

async def create_email_verification_token(
self, user_id: str, email: str, user_context: Dict[str, Any]
self, user_id: str, email: str, tenant_id: str, user_context: Dict[str, Any]
) -> Union[
CreateEmailVerificationTokenOkResult,
CreateEmailVerificationTokenEmailAlreadyVerifiedError,
]:
data = {"userId": user_id, "email": email}
response = await self.querier.send_post_request(
NormalisedURLPath("/recipe/user/email/verify/token"), data
NormalisedURLPath(f"{tenant_id}/recipe/user/email/verify/token"), data
)
if "status" in response and response["status"] == "OK":
return CreateEmailVerificationTokenOkResult(response["token"])
return CreateEmailVerificationTokenEmailAlreadyVerifiedError()

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

async def revoke_email_verification_tokens(
self, user_id: str, email: str, user_context: Dict[str, Any]
self, user_id: str, email: str, tenant_id: str, user_context: Dict[str, Any]
) -> RevokeEmailVerificationTokensOkResult:
data = {"userId": user_id, "email": email}
await self.querier.send_post_request(
NormalisedURLPath("/recipe/user/email/verify/token/remove"), data
NormalisedURLPath(f"{tenant_id}/recipe/user/email/verify/token/remove"),
data,
)
return RevokeEmailVerificationTokensOkResult()

Expand Down
Loading