Module supertokens_python.plugins

Expand source code
from collections import deque
from typing import (
    TYPE_CHECKING,
    Any,
    Callable,
    Dict,
    List,
    Literal,
    Optional,
    Set,
    TypeVar,
    Union,
    cast,
    runtime_checkable,
)

from packaging.specifiers import SpecifierSet
from packaging.version import Version
from typing_extensions import Protocol

from supertokens_python.constants import VERSION
from supertokens_python.framework.request import BaseRequest
from supertokens_python.framework.response import BaseResponse
from supertokens_python.logger import log_debug_message
from supertokens_python.post_init_callbacks import PostSTInitCallbacks
from supertokens_python.recipe.session.interfaces import (
    SessionClaimValidator,
    SessionContainer,
)
from supertokens_python.types import MaybeAwaitable
from supertokens_python.types.base import UserContext
from supertokens_python.types.config import (
    BaseConfig,
    BaseConfigWithoutAPIOverride,
    BaseOverrideConfig,
    BaseOverrideConfigWithoutAPI,
)
from supertokens_python.types.recipe import BaseAPIInterface, BaseRecipeInterface
from supertokens_python.types.response import CamelCaseBaseModel

if TYPE_CHECKING:
    from supertokens_python.recipe.accountlinking.types import AccountLinkingConfig
    from supertokens_python.recipe.dashboard.utils import DashboardConfig
    from supertokens_python.recipe.emailpassword.utils import EmailPasswordConfig
    from supertokens_python.recipe.emailverification.utils import (
        EmailVerificationConfig,
    )
    from supertokens_python.recipe.jwt.utils import JWTConfig
    from supertokens_python.recipe.multifactorauth.types import MultiFactorAuthConfig
    from supertokens_python.recipe.multitenancy.utils import MultitenancyConfig
    from supertokens_python.recipe.oauth2provider.utils import OAuth2ProviderConfig
    from supertokens_python.recipe.openid.utils import OpenIdConfig
    from supertokens_python.recipe.passwordless.utils import PasswordlessConfig
    from supertokens_python.recipe.saml.utils import SAMLConfig
    from supertokens_python.recipe.session.utils import SessionConfig
    from supertokens_python.recipe.thirdparty.utils import ThirdPartyConfig
    from supertokens_python.recipe.totp.types import TOTPConfig
    from supertokens_python.recipe.usermetadata.utils import UserMetadataConfig
    from supertokens_python.recipe.userroles.utils import UserRolesConfig
    from supertokens_python.recipe.webauthn.types.config import WebauthnConfig
    from supertokens_python.supertokens import SupertokensPublicConfig

RecipeConfigType = TypeVar(
    "RecipeConfigType",
    bound=Union[
        "AccountLinkingConfig",
        "DashboardConfig",
        "EmailPasswordConfig",
        "EmailVerificationConfig",
        "JWTConfig",
        "MultiFactorAuthConfig",
        "MultitenancyConfig",
        "OAuth2ProviderConfig",
        "OpenIdConfig",
        "PasswordlessConfig",
        "SessionConfig",
        "ThirdPartyConfig",
        "TOTPConfig",
        "UserMetadataConfig",
        "SAMLConfig",
        "UserRolesConfig",
        "WebauthnConfig",
    ],
)


RecipeInterfaceType = TypeVar("RecipeInterfaceType", bound=BaseRecipeInterface)
APIInterfaceType = TypeVar("APIInterfaceType", bound=BaseAPIInterface)


class RecipeInitRequiredFunction(Protocol):
    def __call__(self, sdk_version: str) -> bool: ...


class RecipePluginOverride:
    functions: Optional[Callable[[BaseRecipeInterface], BaseRecipeInterface]] = None
    apis: Optional[Callable[[BaseAPIInterface], BaseAPIInterface]] = None
    config: Optional[Callable[[Any], Any]] = None
    recipe_init_required: Optional[Union[bool, RecipeInitRequiredFunction]] = None


class PluginRouteHandlerResponse(CamelCaseBaseModel):
    status: int
    body: Any


@runtime_checkable
class PluginRouteHandlerHandlerFunction(Protocol):
    async def __call__(
        self,
        request: BaseRequest,
        response: BaseResponse,
        session: Optional["SessionContainer"],
        user_context: UserContext,
    ) -> BaseResponse: ...


@runtime_checkable
class OverrideGlobalClaimValidatorsFunction(Protocol):
    def __call__(
        self,
        global_claim_validators: List["SessionClaimValidator"],
        session: "SessionContainer",
        user_context: UserContext,
    ) -> MaybeAwaitable[List["SessionClaimValidator"]]: ...


class VerifySessionOptions(CamelCaseBaseModel):
    session_required: bool
    anti_csrf_check: Optional[bool] = None
    check_database: bool
    override_global_claim_validators: Optional[
        OverrideGlobalClaimValidatorsFunction
    ] = None


class PluginRouteHandler(CamelCaseBaseModel):
    method: str
    path: str
    handler: PluginRouteHandlerHandlerFunction
    verify_session_options: Optional[VerifySessionOptions]


class PluginRouteHandlerWithPluginId(PluginRouteHandler):
    plugin_id: str
    """
    This is useful when multiple plugins handle the same route.
    """

    @classmethod
    def from_route_handler(
        cls,
        route_handler: PluginRouteHandler,
        plugin_id: str,
    ):
        return cls(
            **route_handler.model_dump(),
            plugin_id=plugin_id,
        )


@runtime_checkable
class SuperTokensPluginInit(Protocol):
    def __call__(
        self,
        config: "SupertokensPublicConfig",
        all_plugins: List["SuperTokensPublicPlugin"],
        sdk_version: str,
    ) -> None: ...


class PluginDependenciesOkResponse(CamelCaseBaseModel):
    status: Literal["OK"] = "OK"
    plugins_to_add: List["SuperTokensPlugin"]


class PluginDependenciesErrorResponse(CamelCaseBaseModel):
    status: Literal["ERROR"] = "ERROR"
    message: str


@runtime_checkable
class SuperTokensPluginDependencies(Protocol):
    def __call__(
        self,
        config: "SupertokensPublicConfig",
        plugins_above: List["SuperTokensPublicPlugin"],
        sdk_version: str,
    ) -> Union[PluginDependenciesOkResponse, PluginDependenciesErrorResponse]: ...


class PluginRouteHandlerFunctionOkResponse(CamelCaseBaseModel):
    status: Literal["OK"] = "OK"
    route_handlers: List[PluginRouteHandler]


class PluginRouteHandlerFunctionErrorResponse(CamelCaseBaseModel):
    status: Literal["ERROR"] = "ERROR"
    message: str


@runtime_checkable
class PluginRouteHandlerFunction(Protocol):
    def __call__(
        self,
        config: "SupertokensPublicConfig",
        all_plugins: List["SuperTokensPublicPlugin"],
        sdk_version: str,
    ) -> Union[
        PluginRouteHandlerFunctionOkResponse, PluginRouteHandlerFunctionErrorResponse
    ]: ...


@runtime_checkable
class PluginConfig(Protocol):
    def __call__(
        self, config: "SupertokensPublicConfig"
    ) -> "SupertokensPublicConfig": ...


class SuperTokensPluginBase(CamelCaseBaseModel):
    id: str
    version: Optional[str] = None
    compatible_sdk_versions: Union[str, List[str]]
    exports: Optional[Dict[str, Any]] = None


OverrideMap = Dict[str, RecipePluginOverride]


class SuperTokensPlugin(SuperTokensPluginBase):
    init: Optional[SuperTokensPluginInit] = None
    dependencies: Optional[SuperTokensPluginDependencies] = None
    override_map: Optional[OverrideMap] = None
    route_handlers: Optional[
        Union[List[PluginRouteHandler], PluginRouteHandlerFunction]
    ] = None
    config: Optional[PluginConfig] = None

    def get_dependencies(
        self,
        public_config: "SupertokensPublicConfig",
        plugins_above: List["SuperTokensPlugin"],
        sdk_version: str,
    ):
        """
        Pre-order DFS traversal to get all dependencies of a plugin.
        """

        def recurse_deps(
            plugin: SuperTokensPlugin,
            deps: Optional[List[SuperTokensPlugin]] = None,
            visited: Optional[Set[str]] = None,
        ) -> List[SuperTokensPlugin]:
            if deps is None:
                deps = []

            if visited is None:
                visited = set()

            if plugin.id in visited:
                return deps
            visited.add(plugin.id)

            if plugin.dependencies is not None:
                # Get all dependencies of the plugin
                dep_result = plugin.dependencies(
                    config=public_config,
                    plugins_above=[
                        SuperTokensPublicPlugin.from_plugin(plugin)
                        for plugin in plugins_above
                    ],
                    sdk_version=sdk_version,
                )

                # Errors fall through
                if isinstance(dep_result, PluginDependenciesErrorResponse):
                    raise Exception(dep_result.message)

                # Recurse through all dependencies and add the resultant plugins to the list
                # Pre-order DFS traversal
                for dep_plugin in dep_result.plugins_to_add:
                    recurse_deps(dep_plugin, deps)

            # Add the current plugin and mark it as visited
            deps.append(plugin)

            return deps

        return recurse_deps(self)


class SuperTokensPublicPlugin(SuperTokensPluginBase):
    initialized: bool

    @classmethod
    def from_plugin(cls, plugin: SuperTokensPlugin) -> "SuperTokensPublicPlugin":
        return cls(
            id=plugin.id,
            initialized=plugin.init is None,
            version=plugin.version,
            exports=plugin.exports,
            compatible_sdk_versions=plugin.compatible_sdk_versions,
        )


def apply_plugins(
    recipe_id: str,
    config: RecipeConfigType,
    plugins: List[OverrideMap],
) -> RecipeConfigType:
    if not isinstance(config, (BaseConfig, BaseConfigWithoutAPIOverride)):  # type: ignore
        raise TypeError(
            f"Expected config to be an instance of BaseConfig or BaseConfigWithoutAPIOverride. {recipe_id=} {config=}"
        )

    def default_fn_override(
        original_implementation: RecipeInterfaceType,
    ) -> RecipeInterfaceType:
        return original_implementation

    def default_api_override(
        original_implementation: APIInterfaceType,
    ) -> APIInterfaceType:
        return original_implementation

    if config.override is None:
        if isinstance(config, BaseConfigWithoutAPIOverride):
            config.override = BaseOverrideConfigWithoutAPI()
        else:
            config.override = BaseOverrideConfig()  # type: ignore - generic type invariance

    function_overrides = getattr(config.override, "functions", default_fn_override)
    api_overrides = getattr(config.override, "apis", default_api_override)

    function_layers: deque[Any] = deque()
    api_layers: deque[Any] = deque()

    # If we have plugins like 4->3->(2, 1) along with a recipe override,
    # we want to load/init them as: override, 2, 1, 3, 4
    # and call them as: override, 4, 3, 2, 1, original
    # Order of 1/2 does not matter since they are independent from each other.

    for plugin in plugins:
        overrides = plugin.get(recipe_id)
        if overrides is not None:
            if overrides.config is not None:
                overrideable_config = config.to_overrideable_config()
                overridden_config = overrides.config(overrideable_config)

                config = config.from_overrideable_config(
                    overrideable_config=overridden_config,
                )  # type: ignore - various config types used, but will never pass config from a different recipe

            if overrides.functions is not None:
                function_layers.append(overrides.functions)
            if overrides.apis is not None:
                api_layers.append(overrides.apis)

    if function_overrides is not None:
        function_layers.append(function_overrides)
    if api_overrides is not None:
        api_layers.append(api_overrides)

    # Apply overrides in reverse order of definition
    # Plugins: [plugin1, plugin2] would be applied as [override, plugin2, plugin1, original]
    if len(function_layers) > 0:

        def fn_override(
            original_implementation: RecipeInterfaceType,
        ) -> RecipeInterfaceType:
            # The layers will get called in reversed order
            for function_layer in function_layers:
                original_implementation = function_layer(original_implementation)
            return original_implementation

        config.override.functions = fn_override  # type: ignore

    if (
        len(api_layers) > 0
        # AccountLinking recipe does not have an API implementation, uses `BaseConfigWithoutAPIOverride` as base
        and recipe_id != "accountlinking"
        # `BaseConfig` is the base class for all configs with an API override.
        and isinstance(config, BaseConfig)
    ):

        def api_override(original_implementation: APIInterfaceType) -> APIInterfaceType:
            for api_layer in api_layers:
                original_implementation = api_layer(original_implementation)
            return original_implementation

        config.override.apis = api_override  # type: ignore

    return config


class LoadPluginsResponse(CamelCaseBaseModel):
    public_config: "SupertokensPublicConfig"
    processed_plugins: List[SuperTokensPublicPlugin]
    plugin_route_handlers: List[PluginRouteHandlerWithPluginId]
    override_maps: List[OverrideMap]


def load_plugins(
    plugins: List[SuperTokensPlugin],
    public_config: "SupertokensPublicConfig",
) -> LoadPluginsResponse:
    input_plugin_seen_list: Set[str] = set()
    final_plugin_list: List[SuperTokensPlugin] = []
    plugin_route_handlers: List[PluginRouteHandlerWithPluginId] = []

    for plugin in plugins:
        if plugin.id in input_plugin_seen_list:
            log_debug_message(f"Skipping {plugin.id=} as it has already been added")
            continue

        if isinstance(plugin.compatible_sdk_versions, list):
            version_constraints = ",".join(plugin.compatible_sdk_versions)
        else:
            version_constraints = plugin.compatible_sdk_versions

        if not SpecifierSet(version_constraints).contains(Version(VERSION)):
            raise Exception(
                f"Incompatible SDK version for plugin {plugin.id}. "
                f"Version {VERSION} not found in compatible versions {version_constraints}"
            )

        # TODO: Overkill, but could topologically sort the plugins based on dependencies
        dependencies = plugin.get_dependencies(
            public_config=public_config,
            plugins_above=final_plugin_list,
            sdk_version=VERSION,
        )
        final_plugin_list.extend(dependencies)
        input_plugin_seen_list.update({dep.id for dep in dependencies})

    # Secondary check to ensure no duplicate plugins
    # Should ideally be handled in the dependency resolution above.
    unique_plugins: Set[str] = set()
    duplicate_plugins: List[str] = []
    for plugin in final_plugin_list:
        if plugin.id in unique_plugins:
            duplicate_plugins.append(plugin.id)
        unique_plugins.add(plugin.id)

    if len(duplicate_plugins) > 0:
        raise Exception(f"Duplicate plugins found: {', '.join(duplicate_plugins)}")

    processed_plugin_list = [
        SuperTokensPublicPlugin.from_plugin(plugin) for plugin in final_plugin_list
    ]

    for plugin_idx, plugin in enumerate(final_plugin_list):
        # Override the public supertokens config using the config override defined in the plugin
        if plugin.config is not None:
            public_config = plugin.config(public_config)

        if plugin.route_handlers is not None:
            handlers: List[PluginRouteHandler] = []

            if callable(plugin.route_handlers):
                handler_result = plugin.route_handlers(
                    config=public_config,
                    all_plugins=processed_plugin_list,
                    sdk_version=VERSION,
                )
                if isinstance(handler_result, PluginRouteHandlerFunctionErrorResponse):
                    raise Exception(handler_result.message)

                handlers = handler_result.route_handlers
            else:
                handlers = plugin.route_handlers

            plugin_route_handlers.extend(
                [
                    PluginRouteHandlerWithPluginId.from_route_handler(
                        handler, plugin.id
                    )
                    for handler in handlers
                ]
            )

        if plugin.init is not None:

            def callback_factory():
                # This has to be part of the factory to ensure we pick up the correct plugin
                init_fn = cast(SuperTokensPluginInit, plugin.init)
                idx = plugin_idx

                def callback():
                    init_fn(
                        config=public_config,
                        all_plugins=processed_plugin_list,
                        sdk_version=VERSION,
                    )
                    processed_plugin_list[idx].initialized = True

                return callback

            PostSTInitCallbacks.add_post_init_callback(callback_factory())

    override_maps = [
        plugin.override_map
        for plugin in final_plugin_list
        if plugin.override_map is not None
    ]

    return LoadPluginsResponse(
        public_config=public_config,
        processed_plugins=processed_plugin_list,
        plugin_route_handlers=plugin_route_handlers,
        override_maps=override_maps,
    )

Functions

def apply_plugins(recipe_id: str, config: ~RecipeConfigType, plugins: List[Dict[str, RecipePluginOverride]]) ‑> ~RecipeConfigType
def load_plugins(plugins: List[SuperTokensPlugin], public_config: SupertokensPublicConfig)

Classes

class LoadPluginsResponse (**data: Any)

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

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.

Expand source code
class LoadPluginsResponse(CamelCaseBaseModel):
    public_config: "SupertokensPublicConfig"
    processed_plugins: List[SuperTokensPublicPlugin]
    plugin_route_handlers: List[PluginRouteHandlerWithPluginId]
    override_maps: List[OverrideMap]

Ancestors

Class variables

var override_maps : List[Dict[str, RecipePluginOverride]]

The type of the None singleton.

var plugin_route_handlers : List[PluginRouteHandlerWithPluginId]

The type of the None singleton.

var processed_plugins : List[SuperTokensPublicPlugin]

The type of the None singleton.

var public_config : SupertokensPublicConfig

The type of the None singleton.

Inherited members

class OverrideGlobalClaimValidatorsFunction (*args, **kwargs)

Base class for protocol classes.

Protocol classes are defined as::

class Proto(Protocol):
    def meth(self) -> int:
        ...

Such classes are primarily used with static type checkers that recognize structural subtyping (static duck-typing).

For example::

class C:
    def meth(self) -> int:
        return 0

def func(x: Proto) -> int:
    return x.meth()

func(C())  # Passes static type check

See PEP 544 for details. Protocol classes decorated with @typing.runtime_checkable act as simple-minded runtime protocols that check only the presence of given attributes, ignoring their type signatures. Protocol classes can be generic, they are defined as::

class GenProto[T](Protocol):
    def meth(self) -> T:
        ...
Expand source code
@runtime_checkable
class OverrideGlobalClaimValidatorsFunction(Protocol):
    def __call__(
        self,
        global_claim_validators: List["SessionClaimValidator"],
        session: "SessionContainer",
        user_context: UserContext,
    ) -> MaybeAwaitable[List["SessionClaimValidator"]]: ...

Ancestors

  • typing.Protocol
  • typing.Generic
class PluginConfig (*args, **kwargs)

Base class for protocol classes.

Protocol classes are defined as::

class Proto(Protocol):
    def meth(self) -> int:
        ...

Such classes are primarily used with static type checkers that recognize structural subtyping (static duck-typing).

For example::

class C:
    def meth(self) -> int:
        return 0

def func(x: Proto) -> int:
    return x.meth()

func(C())  # Passes static type check

See PEP 544 for details. Protocol classes decorated with @typing.runtime_checkable act as simple-minded runtime protocols that check only the presence of given attributes, ignoring their type signatures. Protocol classes can be generic, they are defined as::

class GenProto[T](Protocol):
    def meth(self) -> T:
        ...
Expand source code
@runtime_checkable
class PluginConfig(Protocol):
    def __call__(
        self, config: "SupertokensPublicConfig"
    ) -> "SupertokensPublicConfig": ...

Ancestors

  • typing.Protocol
  • typing.Generic
class PluginDependenciesErrorResponse (**data: Any)

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

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.

Expand source code
class PluginDependenciesErrorResponse(CamelCaseBaseModel):
    status: Literal["ERROR"] = "ERROR"
    message: str

Ancestors

Class variables

var message : str

The type of the None singleton.

var status : Literal['ERROR']

The type of the None singleton.

Inherited members

class PluginDependenciesOkResponse (**data: Any)

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

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.

Expand source code
class PluginDependenciesOkResponse(CamelCaseBaseModel):
    status: Literal["OK"] = "OK"
    plugins_to_add: List["SuperTokensPlugin"]

Ancestors

Class variables

var plugins_to_add : List[SuperTokensPlugin]

The type of the None singleton.

var status : Literal['OK']

The type of the None singleton.

Inherited members

class PluginRouteHandler (**data: Any)

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

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.

Expand source code
class PluginRouteHandler(CamelCaseBaseModel):
    method: str
    path: str
    handler: PluginRouteHandlerHandlerFunction
    verify_session_options: Optional[VerifySessionOptions]

Ancestors

Subclasses

Class variables

var handlerPluginRouteHandlerHandlerFunction

The type of the None singleton.

var method : str

The type of the None singleton.

var path : str

The type of the None singleton.

var verify_session_options : Optional[VerifySessionOptions]

The type of the None singleton.

Inherited members

class PluginRouteHandlerFunction (*args, **kwargs)

Base class for protocol classes.

Protocol classes are defined as::

class Proto(Protocol):
    def meth(self) -> int:
        ...

Such classes are primarily used with static type checkers that recognize structural subtyping (static duck-typing).

For example::

class C:
    def meth(self) -> int:
        return 0

def func(x: Proto) -> int:
    return x.meth()

func(C())  # Passes static type check

See PEP 544 for details. Protocol classes decorated with @typing.runtime_checkable act as simple-minded runtime protocols that check only the presence of given attributes, ignoring their type signatures. Protocol classes can be generic, they are defined as::

class GenProto[T](Protocol):
    def meth(self) -> T:
        ...
Expand source code
@runtime_checkable
class PluginRouteHandlerFunction(Protocol):
    def __call__(
        self,
        config: "SupertokensPublicConfig",
        all_plugins: List["SuperTokensPublicPlugin"],
        sdk_version: str,
    ) -> Union[
        PluginRouteHandlerFunctionOkResponse, PluginRouteHandlerFunctionErrorResponse
    ]: ...

Ancestors

  • typing.Protocol
  • typing.Generic
class PluginRouteHandlerFunctionErrorResponse (**data: Any)

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

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.

Expand source code
class PluginRouteHandlerFunctionErrorResponse(CamelCaseBaseModel):
    status: Literal["ERROR"] = "ERROR"
    message: str

Ancestors

Class variables

var message : str

The type of the None singleton.

var status : Literal['ERROR']

The type of the None singleton.

Inherited members

class PluginRouteHandlerFunctionOkResponse (**data: Any)

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

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.

Expand source code
class PluginRouteHandlerFunctionOkResponse(CamelCaseBaseModel):
    status: Literal["OK"] = "OK"
    route_handlers: List[PluginRouteHandler]

Ancestors

Class variables

var route_handlers : List[PluginRouteHandler]

The type of the None singleton.

var status : Literal['OK']

The type of the None singleton.

Inherited members

class PluginRouteHandlerHandlerFunction (*args, **kwargs)

Base class for protocol classes.

Protocol classes are defined as::

class Proto(Protocol):
    def meth(self) -> int:
        ...

Such classes are primarily used with static type checkers that recognize structural subtyping (static duck-typing).

For example::

class C:
    def meth(self) -> int:
        return 0

def func(x: Proto) -> int:
    return x.meth()

func(C())  # Passes static type check

See PEP 544 for details. Protocol classes decorated with @typing.runtime_checkable act as simple-minded runtime protocols that check only the presence of given attributes, ignoring their type signatures. Protocol classes can be generic, they are defined as::

class GenProto[T](Protocol):
    def meth(self) -> T:
        ...
Expand source code
@runtime_checkable
class PluginRouteHandlerHandlerFunction(Protocol):
    async def __call__(
        self,
        request: BaseRequest,
        response: BaseResponse,
        session: Optional["SessionContainer"],
        user_context: UserContext,
    ) -> BaseResponse: ...

Ancestors

  • typing.Protocol
  • typing.Generic
class PluginRouteHandlerResponse (**data: Any)

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

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.

Expand source code
class PluginRouteHandlerResponse(CamelCaseBaseModel):
    status: int
    body: Any

Ancestors

Class variables

var body : Any

The type of the None singleton.

var status : int

The type of the None singleton.

Inherited members

class PluginRouteHandlerWithPluginId (**data: Any)

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

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.

Expand source code
class PluginRouteHandlerWithPluginId(PluginRouteHandler):
    plugin_id: str
    """
    This is useful when multiple plugins handle the same route.
    """

    @classmethod
    def from_route_handler(
        cls,
        route_handler: PluginRouteHandler,
        plugin_id: str,
    ):
        return cls(
            **route_handler.model_dump(),
            plugin_id=plugin_id,
        )

Ancestors

Class variables

var plugin_id : str

This is useful when multiple plugins handle the same route.

Static methods

def from_route_handler(route_handler: PluginRouteHandler, plugin_id: str)

Inherited members

class RecipeInitRequiredFunction (*args, **kwargs)

Base class for protocol classes.

Protocol classes are defined as::

class Proto(Protocol):
    def meth(self) -> int:
        ...

Such classes are primarily used with static type checkers that recognize structural subtyping (static duck-typing).

For example::

class C:
    def meth(self) -> int:
        return 0

def func(x: Proto) -> int:
    return x.meth()

func(C())  # Passes static type check

See PEP 544 for details. Protocol classes decorated with @typing.runtime_checkable act as simple-minded runtime protocols that check only the presence of given attributes, ignoring their type signatures. Protocol classes can be generic, they are defined as::

class GenProto[T](Protocol):
    def meth(self) -> T:
        ...
Expand source code
class RecipeInitRequiredFunction(Protocol):
    def __call__(self, sdk_version: str) -> bool: ...

Ancestors

  • typing.Protocol
  • typing.Generic
class RecipePluginOverride
Expand source code
class RecipePluginOverride:
    functions: Optional[Callable[[BaseRecipeInterface], BaseRecipeInterface]] = None
    apis: Optional[Callable[[BaseAPIInterface], BaseAPIInterface]] = None
    config: Optional[Callable[[Any], Any]] = None
    recipe_init_required: Optional[Union[bool, RecipeInitRequiredFunction]] = None

Class variables

var apis : Optional[Callable[[BaseAPIInterface], BaseAPIInterface]]

The type of the None singleton.

var config : Optional[Callable[[Any], Any]]

The type of the None singleton.

var functions : Optional[Callable[[BaseRecipeInterface], BaseRecipeInterface]]

The type of the None singleton.

var recipe_init_required : Union[bool, RecipeInitRequiredFunction, ForwardRef(None)]

The type of the None singleton.

class SuperTokensPlugin (**data: Any)

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

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.

Expand source code
class SuperTokensPlugin(SuperTokensPluginBase):
    init: Optional[SuperTokensPluginInit] = None
    dependencies: Optional[SuperTokensPluginDependencies] = None
    override_map: Optional[OverrideMap] = None
    route_handlers: Optional[
        Union[List[PluginRouteHandler], PluginRouteHandlerFunction]
    ] = None
    config: Optional[PluginConfig] = None

    def get_dependencies(
        self,
        public_config: "SupertokensPublicConfig",
        plugins_above: List["SuperTokensPlugin"],
        sdk_version: str,
    ):
        """
        Pre-order DFS traversal to get all dependencies of a plugin.
        """

        def recurse_deps(
            plugin: SuperTokensPlugin,
            deps: Optional[List[SuperTokensPlugin]] = None,
            visited: Optional[Set[str]] = None,
        ) -> List[SuperTokensPlugin]:
            if deps is None:
                deps = []

            if visited is None:
                visited = set()

            if plugin.id in visited:
                return deps
            visited.add(plugin.id)

            if plugin.dependencies is not None:
                # Get all dependencies of the plugin
                dep_result = plugin.dependencies(
                    config=public_config,
                    plugins_above=[
                        SuperTokensPublicPlugin.from_plugin(plugin)
                        for plugin in plugins_above
                    ],
                    sdk_version=sdk_version,
                )

                # Errors fall through
                if isinstance(dep_result, PluginDependenciesErrorResponse):
                    raise Exception(dep_result.message)

                # Recurse through all dependencies and add the resultant plugins to the list
                # Pre-order DFS traversal
                for dep_plugin in dep_result.plugins_to_add:
                    recurse_deps(dep_plugin, deps)

            # Add the current plugin and mark it as visited
            deps.append(plugin)

            return deps

        return recurse_deps(self)

Ancestors

Class variables

var config : Optional[PluginConfig]

The type of the None singleton.

var dependencies : Optional[SuperTokensPluginDependencies]

The type of the None singleton.

var init : Optional[SuperTokensPluginInit]

The type of the None singleton.

var override_map : Optional[Dict[str, RecipePluginOverride]]

The type of the None singleton.

var route_handlers : Union[List[PluginRouteHandler], PluginRouteHandlerFunction, ForwardRef(None)]

The type of the None singleton.

Methods

def get_dependencies(self, public_config: SupertokensPublicConfig, plugins_above: List[ForwardRef('SuperTokensPlugin')], sdk_version: str)

Pre-order DFS traversal to get all dependencies of a plugin.

Inherited members

class SuperTokensPluginBase (**data: Any)

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

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.

Expand source code
class SuperTokensPluginBase(CamelCaseBaseModel):
    id: str
    version: Optional[str] = None
    compatible_sdk_versions: Union[str, List[str]]
    exports: Optional[Dict[str, Any]] = None

Ancestors

Subclasses

Class variables

var compatible_sdk_versions : Union[str, List[str]]

The type of the None singleton.

var exports : Optional[Dict[str, Any]]

The type of the None singleton.

var id : str

The type of the None singleton.

var version : Optional[str]

The type of the None singleton.

Inherited members

class SuperTokensPluginDependencies (*args, **kwargs)

Base class for protocol classes.

Protocol classes are defined as::

class Proto(Protocol):
    def meth(self) -> int:
        ...

Such classes are primarily used with static type checkers that recognize structural subtyping (static duck-typing).

For example::

class C:
    def meth(self) -> int:
        return 0

def func(x: Proto) -> int:
    return x.meth()

func(C())  # Passes static type check

See PEP 544 for details. Protocol classes decorated with @typing.runtime_checkable act as simple-minded runtime protocols that check only the presence of given attributes, ignoring their type signatures. Protocol classes can be generic, they are defined as::

class GenProto[T](Protocol):
    def meth(self) -> T:
        ...
Expand source code
@runtime_checkable
class SuperTokensPluginDependencies(Protocol):
    def __call__(
        self,
        config: "SupertokensPublicConfig",
        plugins_above: List["SuperTokensPublicPlugin"],
        sdk_version: str,
    ) -> Union[PluginDependenciesOkResponse, PluginDependenciesErrorResponse]: ...

Ancestors

  • typing.Protocol
  • typing.Generic
class SuperTokensPluginInit (*args, **kwargs)

Base class for protocol classes.

Protocol classes are defined as::

class Proto(Protocol):
    def meth(self) -> int:
        ...

Such classes are primarily used with static type checkers that recognize structural subtyping (static duck-typing).

For example::

class C:
    def meth(self) -> int:
        return 0

def func(x: Proto) -> int:
    return x.meth()

func(C())  # Passes static type check

See PEP 544 for details. Protocol classes decorated with @typing.runtime_checkable act as simple-minded runtime protocols that check only the presence of given attributes, ignoring their type signatures. Protocol classes can be generic, they are defined as::

class GenProto[T](Protocol):
    def meth(self) -> T:
        ...
Expand source code
@runtime_checkable
class SuperTokensPluginInit(Protocol):
    def __call__(
        self,
        config: "SupertokensPublicConfig",
        all_plugins: List["SuperTokensPublicPlugin"],
        sdk_version: str,
    ) -> None: ...

Ancestors

  • typing.Protocol
  • typing.Generic
class SuperTokensPublicPlugin (**data: Any)

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

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.

Expand source code
class SuperTokensPublicPlugin(SuperTokensPluginBase):
    initialized: bool

    @classmethod
    def from_plugin(cls, plugin: SuperTokensPlugin) -> "SuperTokensPublicPlugin":
        return cls(
            id=plugin.id,
            initialized=plugin.init is None,
            version=plugin.version,
            exports=plugin.exports,
            compatible_sdk_versions=plugin.compatible_sdk_versions,
        )

Ancestors

Class variables

var initialized : bool

The type of the None singleton.

Static methods

def from_plugin(plugin: SuperTokensPlugin) ‑> SuperTokensPublicPlugin

Inherited members

class VerifySessionOptions (**data: Any)

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

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.

Expand source code
class VerifySessionOptions(CamelCaseBaseModel):
    session_required: bool
    anti_csrf_check: Optional[bool] = None
    check_database: bool
    override_global_claim_validators: Optional[
        OverrideGlobalClaimValidatorsFunction
    ] = None

Ancestors

Class variables

var anti_csrf_check : Optional[bool]

The type of the None singleton.

var check_database : bool

The type of the None singleton.

var override_global_claim_validators : Optional[OverrideGlobalClaimValidatorsFunction]

The type of the None singleton.

var session_required : bool

The type of the None singleton.

Inherited members