Module supertokens_python.recipe.passwordless

Expand source code
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
#
# This software is licensed under the Apache License, Version 2.0 (the
# "License") as published by the Apache Software Foundation.
#
# You may not use this file except in compliance with the License. You may
# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# 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 __future__ import annotations

from typing import TYPE_CHECKING, Any, Awaitable, Callable, Dict, Union

from typing_extensions import Literal

from supertokens_python.ingredients.emaildelivery.types import EmailDeliveryConfig
from supertokens_python.ingredients.smsdelivery.types import SMSDeliveryConfig
from supertokens_python.recipe.passwordless.types import (
    EmailTemplateVars,
    SMSTemplateVars,
)

from .emaildelivery.services import SMTPService
from .recipe import PasswordlessRecipe
from .smsdelivery.services import SuperTokensSMSService, TwilioService
from .types import (
    CreateAndSendCustomEmailParameters,
    CreateAndSendCustomTextMessageParameters,
    EmailDeliveryInterface,
    SMSDeliveryInterface,
)
from .utils import (
    ContactConfig,
    ContactEmailOnlyConfig,
    ContactEmailOrPhoneConfig,
    ContactPhoneOnlyConfig,
    InputOverrideConfig,
    PasswordlessOverrideConfig,
    PhoneOrEmailInput,
)

if TYPE_CHECKING:
    from supertokens_python.supertokens import RecipeInit


def init(
    contact_config: ContactConfig,
    flow_type: Literal[
        "USER_INPUT_CODE", "MAGIC_LINK", "USER_INPUT_CODE_AND_MAGIC_LINK"
    ],
    override: Union[PasswordlessOverrideConfig, None] = None,
    get_custom_user_input_code: Union[
        Callable[[str, Dict[str, Any]], Awaitable[str]], None
    ] = None,
    email_delivery: Union[EmailDeliveryConfig[EmailTemplateVars], None] = None,
    sms_delivery: Union[SMSDeliveryConfig[SMSTemplateVars], None] = None,
) -> RecipeInit:
    return PasswordlessRecipe.init(
        contact_config,
        flow_type,
        override,
        get_custom_user_input_code,
        email_delivery,
        sms_delivery,
    )


__all__ = [
    "ContactConfig",
    "ContactEmailOnlyConfig",
    "ContactEmailOrPhoneConfig",
    "ContactPhoneOnlyConfig",
    "CreateAndSendCustomEmailParameters",
    "CreateAndSendCustomTextMessageParameters",
    "EmailDeliveryInterface",
    "InputOverrideConfig",  # deprecated, use PasswordlessOverrideConfig instead
    "PasswordlessOverrideConfig",
    "PhoneOrEmailInput",
    "SMSDeliveryInterface",
    "SMTPService",
    "SuperTokensSMSService",
    "TwilioService",
    "init",
]

Sub-modules

supertokens_python.recipe.passwordless.api
supertokens_python.recipe.passwordless.asyncio
supertokens_python.recipe.passwordless.constants
supertokens_python.recipe.passwordless.emaildelivery
supertokens_python.recipe.passwordless.exceptions
supertokens_python.recipe.passwordless.interfaces
supertokens_python.recipe.passwordless.recipe
supertokens_python.recipe.passwordless.recipe_implementation
supertokens_python.recipe.passwordless.smsdelivery
supertokens_python.recipe.passwordless.syncio
supertokens_python.recipe.passwordless.types
supertokens_python.recipe.passwordless.utils

Functions

def init(contact_config: ContactConfig, flow_type: "Literal['USER_INPUT_CODE', 'MAGIC_LINK', 'USER_INPUT_CODE_AND_MAGIC_LINK']", override: Union[BaseOverrideConfig[RecipeInterface, APIInterface], None] = None, get_custom_user_input_code: Union[Callable[[str, Dict[str, Any]], Awaitable[str]], None] = None, email_delivery: Union[EmailDeliveryConfig[EmailTemplateVars], None] = None, sms_delivery: Union[SMSDeliveryConfig[SMSTemplateVars], None] = None)

Classes

class InputOverrideConfig (**data: Any)

Base class for input override config with API overrides.

Create a new model by parsing and validating input data from keyword arguments.

Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.

self is explicitly positional-only to allow self as a field name.

Ancestors

Class variables

var model_config

The type of the None singleton.

class PasswordlessOverrideConfig (**data: Any)

Base class for input override config with API overrides.

Create a new model by parsing and validating input data from keyword arguments.

Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.

self is explicitly positional-only to allow self as a field name.

Ancestors

Inherited members

class ContactConfig (contact_method: "Literal['PHONE', 'EMAIL', 'EMAIL_OR_PHONE']")

Helper class that provides a standard way to create an ABC using inheritance.

Expand source code
class ContactConfig(ABC):
    def __init__(self, contact_method: Literal["PHONE", "EMAIL", "EMAIL_OR_PHONE"]):
        self.contact_method = contact_method

Ancestors

  • abc.ABC

Subclasses

class ContactEmailOnlyConfig (validate_email_address: Union[Callable[[str, str], Awaitable[Union[str, None]]], None] = None)

Helper class that provides a standard way to create an ABC using inheritance.

Expand source code
class ContactEmailOnlyConfig(ContactConfig):
    def __init__(
        self,
        validate_email_address: Union[
            Callable[[str, str], Awaitable[Union[str, None]]], None
        ] = None,
    ):
        super().__init__("EMAIL")

        if validate_email_address is None:
            self.validate_email_address = default_validate_email
        else:
            self.validate_email_address = validate_email_address

Ancestors

class ContactEmailOrPhoneConfig (validate_email_address: Union[Callable[[str, str], Awaitable[Union[str, None]]], None] = None, validate_phone_number: Union[Callable[[str, str], Awaitable[Union[str, None]]], None] = None)

Helper class that provides a standard way to create an ABC using inheritance.

Expand source code
class ContactEmailOrPhoneConfig(ContactConfig):
    def __init__(
        self,
        validate_email_address: Union[
            Callable[[str, str], Awaitable[Union[str, None]]], None
        ] = None,
        validate_phone_number: Union[
            Callable[[str, str], Awaitable[Union[str, None]]], None
        ] = None,
    ):
        super().__init__("EMAIL_OR_PHONE")

        if validate_email_address is None:
            self.validate_email_address = default_validate_email
        else:
            self.validate_email_address = validate_email_address

        if validate_phone_number is None:
            self.validate_phone_number = default_validate_phone_number
        else:
            self.validate_phone_number = validate_phone_number

Ancestors

class ContactPhoneOnlyConfig (validate_phone_number: Union[Callable[[str, str], Awaitable[Union[str, None]]], None] = None)

Helper class that provides a standard way to create an ABC using inheritance.

Expand source code
class ContactPhoneOnlyConfig(ContactConfig):
    def __init__(
        self,
        validate_phone_number: Union[
            Callable[[str, str], Awaitable[Union[str, None]]], None
        ] = None,
    ):
        super().__init__("PHONE")

        if validate_phone_number is None:
            self.validate_phone_number = default_validate_phone_number
        else:
            self.validate_phone_number = validate_phone_number

Ancestors

class CreateAndSendCustomEmailParameters (tenant_id: str, code_life_time: int, pre_auth_session_id: str, email: str, is_first_factor: bool, user_input_code: Optional[str] = None, url_with_link_code: Optional[str] = None)
Expand source code
class CreateAndSendCustomEmailParameters:
    def __init__(
        self,
        tenant_id: str,
        code_life_time: int,
        pre_auth_session_id: str,
        email: str,
        is_first_factor: bool,
        user_input_code: Union[str, None] = None,
        url_with_link_code: Union[str, None] = None,
    ):
        self.email = email
        self.code_life_time = code_life_time
        self.pre_auth_session_id = pre_auth_session_id
        self.user_input_code = user_input_code
        self.url_with_link_code = url_with_link_code
        self.tenant_id = tenant_id
        self.is_first_factor = is_first_factor
class CreateAndSendCustomTextMessageParameters (tenant_id: str, code_life_time: int, pre_auth_session_id: str, phone_number: str, is_first_factor: bool, user_input_code: Optional[str] = None, url_with_link_code: Optional[str] = None)
Expand source code
class CreateAndSendCustomTextMessageParameters:
    def __init__(
        self,
        tenant_id: str,
        code_life_time: int,
        pre_auth_session_id: str,
        phone_number: str,
        is_first_factor: bool,
        user_input_code: Union[str, None] = None,
        url_with_link_code: Union[str, None] = None,
    ):
        self.code_life_time = code_life_time
        self.pre_auth_session_id = pre_auth_session_id
        self.phone_number = phone_number
        self.user_input_code = user_input_code
        self.url_with_link_code = url_with_link_code
        self.tenant_id = tenant_id
        self.is_first_factor = is_first_factor
class EmailDeliveryInterface

Helper class that provides a standard way to create an ABC using inheritance.

Expand source code
class EmailDeliveryInterface(ABC, Generic[_T]):
    @abstractmethod
    async def send_email(self, template_vars: _T, user_context: Dict[str, Any]) -> None:
        pass

Ancestors

  • abc.ABC
  • typing.Generic

Subclasses

Methods

async def send_email(self, template_vars: _T, user_context: Dict[str, Any]) ‑> None
class PhoneOrEmailInput (phone_number: Union[str, None], email: Union[str, None])
Expand source code
class PhoneOrEmailInput:
    def __init__(self, phone_number: Union[str, None], email: Union[str, None]):
        self.phone_number = phone_number
        self.email = email
class SMSDeliveryInterface

Helper class that provides a standard way to create an ABC using inheritance.

Expand source code
class SMSDeliveryInterface(ABC, Generic[_T]):
    @abstractmethod
    async def send_sms(self, template_vars: _T, user_context: Dict[str, Any]) -> None:
        pass

Ancestors

  • abc.ABC
  • typing.Generic

Subclasses

Methods

async def send_sms(self, template_vars: ~_T, user_context: Dict[str, Any]) ‑> None
class SMTPService (smtp_settings: SMTPSettings, override: Union[Callable[[SMTPOverrideInput], SMTPOverrideInput], None] = None)

Helper class that provides a standard way to create an ABC using inheritance.

Expand source code
class SMTPService(EmailDeliveryInterface[PasswordlessLoginEmailTemplateVars]):
    service_implementation: SMTPServiceInterface[PasswordlessLoginEmailTemplateVars]

    def __init__(
        self,
        smtp_settings: SMTPSettings,
        override: Union[Callable[[SMTPOverrideInput], SMTPOverrideInput], None] = None,
    ) -> None:
        self.transporter = Transporter(smtp_settings)
        oi = ServiceImplementation(self.transporter)
        self.service_implementation = oi if override is None else override(oi)

    async def send_email(
        self,
        template_vars: PasswordlessLoginEmailTemplateVars,
        user_context: Dict[str, Any],
    ) -> None:
        content = await self.service_implementation.get_content(
            template_vars, user_context
        )
        await self.service_implementation.send_raw_email(content, user_context)

Ancestors

Class variables

var service_implementationSMTPServiceInterface[CreateAndSendCustomEmailParameters]

The type of the None singleton.

Methods

async def send_email(self, template_vars: PasswordlessLoginEmailTemplateVars, user_context: Dict[str, Any]) ‑> None
class SuperTokensSMSService (api_key: str)

Helper class that provides a standard way to create an ABC using inheritance.

Expand source code
class SuperTokensSMSService(SMSDeliveryInterface[PasswordlessLoginSMSTemplateVars]):
    def __init__(self, api_key: str) -> None:
        self.api_key = api_key

    async def send_sms(
        self,
        template_vars: PasswordlessLoginSMSTemplateVars,
        user_context: Dict[str, Any],
    ) -> None:
        supertokens = Supertokens.get_instance()
        app_name = supertokens.app_info.app_name

        sms_input = {
            "type": "PASSWORDLESS_LOGIN",
            "phoneNumber": template_vars.phone_number,
            "codeLifetime": template_vars.code_life_time,
            "appName": app_name,
        }
        if template_vars.url_with_link_code:
            sms_input["urlWithLinkCode"] = template_vars.url_with_link_code
        if template_vars.user_input_code:
            sms_input["userInputCode"] = template_vars.user_input_code
        try:
            async with AsyncClient(timeout=30.0) as client:
                await client.post(  # type: ignore
                    SUPERTOKENS_SMS_SERVICE_URL,
                    json={
                        "apiKey": self.api_key,
                        "smsInput": sms_input,
                    },
                    headers={"api-version": "0"},
                )
        except Exception as e:
            log_debug_message("Error sending passwordless login SMS")
            handle_httpx_client_exceptions(e, sms_input)
            raise e

Ancestors

Methods

async def send_sms(self, template_vars: CreateAndSendCustomTextMessageParameters, user_context: Dict[str, Any]) ‑> None
class TwilioService (twilio_settings: TwilioSettings, override: Union[Callable[[TwilioServiceInterface[_T]], TwilioServiceInterface[_T]], None] = None)

Helper class that provides a standard way to create an ABC using inheritance.

Expand source code
class TwilioService(SMSDeliveryInterface[PasswordlessLoginSMSTemplateVars]):
    service_implementation: TwilioServiceInterface[PasswordlessLoginSMSTemplateVars]

    def __init__(
        self,
        twilio_settings: TwilioSettings,
        override: Union[
            Callable[[TwilioServiceInterface[_T]], TwilioServiceInterface[_T]], None
        ] = None,
    ) -> None:
        self.config = normalize_twilio_settings(twilio_settings)
        otps = twilio_settings.opts if twilio_settings.opts else {}
        self.twilio_client = Client(  # type: ignore
            twilio_settings.account_sid, twilio_settings.auth_token, **otps
        )
        oi = ServiceImplementation(self.twilio_client)  # type: ignore
        self.service_implementation = oi if override is None else override(oi)  # type: ignore

    async def send_sms(
        self,
        template_vars: PasswordlessLoginSMSTemplateVars,
        user_context: Dict[str, Any],
    ) -> None:
        content = await self.service_implementation.get_content(
            template_vars, user_context
        )
        await self.service_implementation.send_raw_sms(
            content,
            user_context,
            from_=self.config.from_,
            messaging_service_sid=self.config.messaging_service_sid,
        )

Ancestors

Class variables

var service_implementationTwilioServiceInterface[CreateAndSendCustomTextMessageParameters]

The type of the None singleton.

Methods

async def send_sms(self, template_vars: PasswordlessLoginSMSTemplateVars, user_context: Dict[str, Any]) ‑> None