Skip to content

Commit

Permalink
modifies email verification recipe fully
Browse files Browse the repository at this point in the history
  • Loading branch information
rishabhpoddar committed Aug 8, 2024
1 parent d091f6d commit 9789fd3
Show file tree
Hide file tree
Showing 38 changed files with 601 additions and 286 deletions.
3 changes: 2 additions & 1 deletion .pylintrc
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,8 @@ disable=raw-checker-failed,
logging-fstring-interpolation,
consider-using-f-string,
consider-using-in,
no-else-return
no-else-return,
no-self-use


# Enable the message, report, category or checker with the given id(s). You can
Expand Down
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [unreleased]

### Breaking changes
- `supertokens_python.recipe.emailverification.types.User` has been renamed to `supertokens_python.recipe.emailverification.types.EmailVerificationUser`
- The user object has been changed to be a global one, containing information about all emails, phone numbers, third party info and login methods associated with that user.
- Type of `get_email_for_user_id` in `emailverification.init` has changed

## [0.24.0] - 2024-07-31

### Changes
Expand Down
28 changes: 15 additions & 13 deletions supertokens_python/recipe/accountlinking/recipe.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@
)
from supertokens_python.process_state import PROCESS_STATE, ProcessState
from typing_extensions import Literal
from supertokens_python.recipe.emailverification.recipe import EmailVerificationRecipe

from .types import (
RecipeLevelUser,
Expand All @@ -42,15 +41,14 @@

from .interfaces import RecipeInterface

from supertokens_python.recipe.emailverification.interfaces import (
CreateEmailVerificationTokenOkResult,
)

if TYPE_CHECKING:
from supertokens_python.supertokens import AppInfo
from supertokens_python.types import AccountLinkingUser, LoginMethod, RecipeUserId
from supertokens_python.recipe.session import SessionContainer
from supertokens_python.framework import BaseRequest, BaseResponse
from supertokens_python.recipe.emailverification.recipe import (
EmailVerificationRecipe,
)


class EmailChangeAllowedResult:
Expand Down Expand Up @@ -114,6 +112,13 @@ def __init__(
else self.config.override.functions(recipe_implementation)
)

self.email_verification_recipe: EmailVerificationRecipe | None = None

def register_email_verification_recipe(
self, email_verification_recipe: EmailVerificationRecipe
):
self.email_verification_recipe = email_verification_recipe

def is_error_from_this_recipe_based_on_instance(self, err: Exception) -> bool:
return False

Expand Down Expand Up @@ -693,10 +698,7 @@ async def verify_email_for_recipe_user_if_linked_accounts_are_verified(
recipe_user_id: RecipeUserId,
user_context: Dict[str, Any],
) -> None:
try:
EmailVerificationRecipe.get_instance_or_throw()
except Exception:
# if email verification recipe is not initialized, we do a no-op
if self.email_verification_recipe is None:
return

if user.is_primary_user:
Expand All @@ -718,20 +720,20 @@ async def verify_email_for_recipe_user_if_linked_accounts_are_verified(
break

if should_verify_email:
ev_recipe = EmailVerificationRecipe.get_instance_or_throw()
ev_recipe = self.email_verification_recipe.get_instance_or_throw()
resp = await ev_recipe.recipe_implementation.create_email_verification_token(
tenant_id=user.tenant_ids[0],
user_id=recipe_user_id.get_as_string(),
recipe_user_id=recipe_user_id,
email=recipe_user_email,
user_context=user_context,
)
if isinstance(resp, CreateEmailVerificationTokenOkResult):
if resp.status == "OK":
# we purposely pass in false below cause we don't want account
# linking to happen
await ev_recipe.recipe_implementation.verify_email_using_token(
tenant_id=user.tenant_ids[0],
token=resp.token,
# attempt_account_linking=False,
attempt_account_linking=False,
user_context=user_context,
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
UserEmailVerifyGetAPIResponse,
FeatureNotEnabledError,
)
from supertokens_python.types import RecipeUserId

from typing import Union, Dict, Any

Expand All @@ -28,5 +29,7 @@ async def handle_user_email_verify_get(
except Exception:
return FeatureNotEnabledError()

is_verified = await is_email_verified(user_id, user_context=user_context)
is_verified = await is_email_verified(
RecipeUserId(user_id), user_context=user_context
)
return UserEmailVerifyGetAPIResponse(is_verified)
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
VerifyEmailUsingTokenInvalidTokenError,
)

from supertokens_python.types import RecipeUserId

from ...interfaces import (
APIInterface,
APIOptions,
Expand Down Expand Up @@ -40,7 +42,10 @@ async def handle_user_email_verify_put(

if verified:
token_response = await create_email_verification_token(
tenant_id=tenant_id, user_id=user_id, email=None, user_context=user_context
tenant_id=tenant_id,
recipe_user_id=RecipeUserId(user_id),
email=None,
user_context=user_context,
)

if isinstance(
Expand All @@ -57,6 +62,6 @@ async def handle_user_email_verify_put(
raise Exception("Should not come here")

else:
await unverify_email(user_id, user_context=user_context)
await unverify_email(RecipeUserId(user_id), user_context=user_context)

return UserEmailVerifyPutAPIResponse()
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
UserEmailVerifyTokenPostAPIEmailAlreadyVerifiedErrorResponse,
)

from supertokens_python.types import RecipeUserId


async def handle_email_verify_token_post(
_api_interface: APIInterface,
Expand All @@ -32,7 +34,11 @@ async def handle_email_verify_token_post(
)

res = await send_email_verification_email(
tenant_id=tenant_id, user_id=user_id, email=None, user_context=user_context
tenant_id=tenant_id,
user_id=user_id,
recipe_user_id=RecipeUserId(user_id),
email=None,
user_context=user_context,
)

if isinstance(res, SendEmailVerificationEmailAlreadyVerifiedError):
Expand Down
5 changes: 1 addition & 4 deletions supertokens_python/recipe/emailpassword/recipe.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@

from supertokens_python.exceptions import SuperTokensError, raise_general_exception
from supertokens_python.querier import Querier
from supertokens_python.recipe.emailverification import EmailVerificationRecipe

from .api import (
handle_email_exists_api,
Expand Down Expand Up @@ -118,9 +117,7 @@ def get_emailpassword_config() -> EmailPasswordConfig:
)

def callback():
ev_recipe = EmailVerificationRecipe.get_instance_optional()
if ev_recipe:
ev_recipe.add_get_email_for_user_id_func(self.get_email_for_user_id)
pass

PostSTInitCallbacks.add_post_init_callback(callback)

Expand Down
4 changes: 2 additions & 2 deletions supertokens_python/recipe/emailverification/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,12 +43,12 @@
def init(
mode: MODE_TYPE,
email_delivery: Union[EmailDeliveryConfig[EmailTemplateVars], None] = None,
get_email_for_user_id: Optional[TypeGetEmailForUserIdFunction] = None,
get_email_for_recipe_user_id: Optional[TypeGetEmailForUserIdFunction] = None,
override: Union[OverrideConfig, None] = None,
) -> Callable[[AppInfo], RecipeModule]:
return EmailVerificationRecipe.init(
mode,
email_delivery,
get_email_for_user_id,
get_email_for_recipe_user_id,
override,
)
54 changes: 34 additions & 20 deletions supertokens_python/recipe/emailverification/asyncio/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
)
from supertokens_python.recipe.emailverification.types import EmailTemplateVars
from supertokens_python.recipe.emailverification.recipe import EmailVerificationRecipe

from supertokens_python.types import RecipeUserId
from supertokens_python.recipe.emailverification.utils import get_email_verify_link
from supertokens_python.recipe.emailverification.types import (
VerificationEmailTemplateVars,
Expand All @@ -38,7 +38,7 @@

async def create_email_verification_token(
tenant_id: str,
user_id: str,
recipe_user_id: RecipeUserId,
email: Optional[str] = None,
user_context: Union[None, Dict[str, Any]] = None,
) -> Union[
Expand All @@ -49,7 +49,9 @@ async def create_email_verification_token(
user_context = {}
recipe = EmailVerificationRecipe.get_instance_or_throw()
if email is None:
email_info = await recipe.get_email_for_user_id(user_id, user_context)
email_info = await recipe.get_email_for_recipe_user_id(
None, recipe_user_id, user_context
)
if isinstance(email_info, GetEmailForUserIdOkResult):
email = email_info.email
elif isinstance(email_info, EmailDoesNotExistError):
Expand All @@ -58,22 +60,25 @@ 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, tenant_id, user_context
recipe_user_id, email, tenant_id, user_context
)


async def verify_email_using_token(
tenant_id: str, token: str, user_context: Union[None, Dict[str, Any]] = None
tenant_id: str,
token: str,
attempt_account_linking: bool = True,
user_context: Union[None, Dict[str, Any]] = None,
):
if user_context is None:
user_context = {}
return await EmailVerificationRecipe.get_instance_or_throw().recipe_implementation.verify_email_using_token(
token, tenant_id, user_context
token, tenant_id, attempt_account_linking, user_context
)


async def is_email_verified(
user_id: str,
recipe_user_id: RecipeUserId,
email: Optional[str] = None,
user_context: Union[None, Dict[str, Any]] = None,
):
Expand All @@ -82,7 +87,9 @@ async def is_email_verified(

recipe = EmailVerificationRecipe.get_instance_or_throw()
if email is None:
email_info = await recipe.get_email_for_user_id(user_id, user_context)
email_info = await recipe.get_email_for_recipe_user_id(
None, recipe_user_id, user_context
)
if isinstance(email_info, GetEmailForUserIdOkResult):
email = email_info.email
elif isinstance(email_info, EmailDoesNotExistError):
Expand All @@ -91,13 +98,13 @@ async def is_email_verified(
raise Exception("Unknown User ID provided without email")

return await recipe.recipe_implementation.is_email_verified(
user_id, email, user_context
recipe_user_id, email, user_context
)


async def revoke_email_verification_tokens(
tenant_id: str,
user_id: str,
recipe_user_id: RecipeUserId,
email: Optional[str] = None,
user_context: Optional[Dict[str, Any]] = None,
) -> RevokeEmailVerificationTokensOkResult:
Expand All @@ -106,7 +113,9 @@ async def revoke_email_verification_tokens(

recipe = EmailVerificationRecipe.get_instance_or_throw()
if email is None:
email_info = await recipe.get_email_for_user_id(user_id, user_context)
email_info = await recipe.get_email_for_recipe_user_id(
None, recipe_user_id, user_context
)
if isinstance(email_info, GetEmailForUserIdOkResult):
email = email_info.email
elif isinstance(email_info, EmailDoesNotExistError):
Expand All @@ -115,12 +124,12 @@ async def revoke_email_verification_tokens(
raise Exception("Unknown User ID provided without email")

return await EmailVerificationRecipe.get_instance_or_throw().recipe_implementation.revoke_email_verification_tokens(
user_id, email, tenant_id, user_context
recipe_user_id, email, tenant_id, user_context
)


async def unverify_email(
user_id: str,
recipe_user_id: RecipeUserId,
email: Optional[str] = None,
user_context: Union[None, Dict[str, Any]] = None,
):
Expand All @@ -129,7 +138,9 @@ async def unverify_email(

recipe = EmailVerificationRecipe.get_instance_or_throw()
if email is None:
email_info = await recipe.get_email_for_user_id(user_id, user_context)
email_info = await recipe.get_email_for_recipe_user_id(
None, recipe_user_id, user_context
)
if isinstance(email_info, GetEmailForUserIdOkResult):
email = email_info.email
elif isinstance(email_info, EmailDoesNotExistError):
Expand All @@ -140,7 +151,7 @@ async def unverify_email(
raise Exception("Unknown User ID provided without email")

return await EmailVerificationRecipe.get_instance_or_throw().recipe_implementation.unverify_email(
user_id, email, user_context
recipe_user_id, email, user_context
)


Expand All @@ -157,7 +168,7 @@ async def send_email(

async def create_email_verification_link(
tenant_id: str,
user_id: str,
recipe_user_id: RecipeUserId,
email: Optional[str],
user_context: Optional[Dict[str, Any]] = None,
) -> Union[
Expand All @@ -171,7 +182,7 @@ async def create_email_verification_link(
app_info = recipe_instance.get_app_info()

email_verification_token = await create_email_verification_token(
tenant_id, user_id, email, user_context
tenant_id, recipe_user_id, email, user_context
)
if isinstance(
email_verification_token, CreateEmailVerificationTokenEmailAlreadyVerifiedError
Expand All @@ -193,6 +204,7 @@ async def create_email_verification_link(
async def send_email_verification_email(
tenant_id: str,
user_id: str,
recipe_user_id: RecipeUserId,
email: Optional[str],
user_context: Optional[Dict[str, Any]] = None,
) -> Union[
Expand All @@ -205,7 +217,9 @@ async def send_email_verification_email(
if email is None:
recipe_instance = EmailVerificationRecipe.get_instance_or_throw()

email_info = await recipe_instance.get_email_for_user_id(user_id, user_context)
email_info = await recipe_instance.get_email_for_recipe_user_id(
None, recipe_user_id, user_context
)
if isinstance(email_info, GetEmailForUserIdOkResult):
email = email_info.email
elif isinstance(email_info, EmailDoesNotExistError):
Expand All @@ -214,7 +228,7 @@ async def send_email_verification_email(
raise Exception("Unknown User ID provided without email")

email_verification_link = await create_email_verification_link(
tenant_id, user_id, email, user_context
tenant_id, recipe_user_id, email, user_context
)

if isinstance(
Expand All @@ -224,7 +238,7 @@ async def send_email_verification_email(

await send_email(
VerificationEmailTemplateVars(
user=VerificationEmailTemplateVarsUser(user_id, email),
user=VerificationEmailTemplateVarsUser(user_id, recipe_user_id, email),
email_verify_link=email_verification_link.link,
tenant_id=tenant_id,
),
Expand Down
Loading

0 comments on commit 9789fd3

Please sign in to comment.