Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
3a1bb4a
feat: adds basic plugin functionality
namsnath Jun 5, 2025
5f57509
feat: make plugins functional
namsnath Jun 6, 2025
fc9b529
update: change plugin evaluation order
namsnath Jun 6, 2025
bc6bf7d
update: move plugin logic to function, add config override support
namsnath Jun 10, 2025
f06e08f
update: cleanup load_plugins
namsnath Jun 10, 2025
c317628
test: st config overrides, route handlers
namsnath Jun 10, 2025
8c53552
refactor: move classes to new file
namsnath Jun 10, 2025
b7a47fe
update: import order
namsnath Jun 10, 2025
b37d23e
update: adds tests
namsnath Jun 16, 2025
837b0c3
update: adds init tests
namsnath Jun 18, 2025
fefab28
refactor: use base classes for configs/overrides/interfaces
namsnath Jun 30, 2025
ffff56b
fix: cyclic imports
namsnath Jul 1, 2025
4f43c37
try to make types work with subclasses
namsnath Jul 1, 2025
20b173a
refactor: rename classes for consistency, fix plugin types
namsnath Jul 1, 2025
6354579
fix: accountlinking recipe get_instance, tests
namsnath Jul 2, 2025
28e8121
fix: servers
namsnath Jul 2, 2025
5ccce69
fix: servers
namsnath Jul 2, 2025
b1d66bc
fix: webauthn email ingredient types, bump pyright
namsnath Jul 3, 2025
5a23e04
update: address TODOs
namsnath Jul 21, 2025
397e592
feat: setup common util for config override normalisation
namsnath Jul 21, 2025
1ff70fd
fix: circular imports, add tests for config override
namsnath Jul 23, 2025
44dc8cc
update: standardizes `__init__` exports
namsnath Jul 23, 2025
75fe3b4
lint: fix lint errors
namsnath Jul 23, 2025
4a5e8f2
fix: broken imports
namsnath Jul 23, 2025
3983522
fix: changes test plugin versions to use current SDK version
namsnath Jul 23, 2025
c677c94
fix: SuperTokensInputConfig model rebuild, base `__init__` imports/ex…
namsnath Jul 23, 2025
decc4cd
feat: adds way to check if recipe is initialized
namsnath Jul 24, 2025
e7869a9
feat: use normalised app info in public config
namsnath Jul 25, 2025
5b1f37e
feat: adds additional check for duplicate plugins
namsnath Jul 25, 2025
11a28ca
feat: improves plugin version checks
namsnath Aug 6, 2025
09b4216
feat: add error handling for plugin route handlers
namsnath Aug 6, 2025
fb6e57a
test: adds workflow to test `get-versions-from-repo` action
namsnath Aug 13, 2025
b44c441
update: adds github token to workflows
namsnath Aug 13, 2025
bcd9c72
update: only use versions available in repo for inputs
namsnath Aug 13, 2025
cb599e9
update: check for if outputs can be accessed in workflows
namsnath Aug 13, 2025
3e1bfbf
update: try to use jq
namsnath Aug 13, 2025
d1ab291
update: set all steps to use jq
namsnath Aug 13, 2025
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
92 changes: 92 additions & 0 deletions .github/workflows/test-version-action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
name: Test Versions Action

on:
pull_request:
types:
- opened
- reopened
- synchronize


concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
supported-versions:
runs-on: ubuntu-latest
outputs:
fdiVersions: ${{ steps.versions.outputs.fdiVersions }}
cdiVersions: ${{ steps.versions.outputs.cdiVersions }}
pyVersions: '["3.8", "3.13"]'

steps:
- uses: actions/checkout@v4

- uses: supertokens/get-supported-versions-action@main
id: versions
with:
has-fdi: true
has-cdi: true
has-web-js: false

get-versions:
runs-on: ubuntu-latest
needs: supported-versions

strategy:
fail-fast: false
matrix:
fdi-version: ${{ fromJSON(needs.supported-versions.outputs.fdiVersions) }}
cdi-version: ${{ fromJSON(needs.supported-versions.outputs.cdiVersions) }}


steps:
- uses: supertokens/actions/get-versions-from-repo@main
id: node
with:
repo: supertokens-node
github-token: ${{ secrets.GITHUB_TOKEN }}
cdi-versions: ${{ needs.supported-versions.outputs.cdiVersions }}
fdi-versions: ${{ needs.supported-versions.outputs.fdiVersions }}

- run: |
cdiVersion=$( echo '${{ steps.node.outputs.cdiVersions }}' | jq -r '.["${{ matrix.cdi-version }}"]' )
echo "Node version from CDI: $cdiVersion"
fdiVersion=$( echo '${{ steps.node.outputs.fdiVersions }}' | jq -r '.["${{ matrix.fdi-version }}"]' )
echo "Node version from FDI: $fdiVersion"

- uses: supertokens/actions/get-versions-from-repo@main
id: auth-react
with:
repo: supertokens-auth-react
github-token: ${{ secrets.GITHUB_TOKEN }}
# cdi-versions: ${{ needs.supported-versions.outputs.cdiVersions }}
fdi-versions: ${{ needs.supported-versions.outputs.fdiVersions }}

- run: |
fdiVersion=$( echo '${{ steps.auth-react.outputs.fdiVersions }}' | jq -r '.["${{ matrix.fdi-version }}"]' )
echo "Auth React version from FDI: $fdiVersion"

- uses: supertokens/actions/get-versions-from-repo@main
id: website
with:
repo: supertokens-website
github-token: ${{ secrets.GITHUB_TOKEN }}
# cdi-versions: ${{ needs.supported-versions.outputs.cdiVersions }}
fdi-versions: ${{ needs.supported-versions.outputs.fdiVersions }}

- run: |
fdiVersion=$( echo '${{ steps.website.outputs.fdiVersions }}' | jq -r '.["${{ matrix.fdi-version }}"]' )
echo "website version from FDI: $fdiVersion"

- uses: supertokens/actions/get-versions-from-repo@main
id: core
with:
repo: supertokens-core
github-token: ${{ secrets.GITHUB_TOKEN }}
cdi-versions: ${{ needs.supported-versions.outputs.cdiVersions }}
# fdi-versions: ${{ needs.supported-versions.outputs.fdiVersions }}
- run: |
cdiVersion=$( echo '${{ steps.core.outputs.cdiVersions }}' | jq -r '.["${{ matrix.cdi-version }}"]' )
echo "core version from CDI: $cdiVersion"
44 changes: 44 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,50 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [unreleased]

## [0.31.0] - 2025-07-18
### Adds plugins support
- Adds an `experimental` property (`SuperTokensExperimentalConfig`) to the `SuperTokensConfig`
- Plugins can be configured under using the `plugins` property in the `experimental` config
- Refactors the AccountLinking recipe to be automatically initialized on SuperTokens init
- Adds `is_recipe_initialized` method to check if a recipe has been initialized

### Breaking Changes
- `AccountLinkingRecipe.get_instance` will now raise an exception if not initialized
- Various config classes renamed for consistency across the codebase, and classes added where they were missing
- Old classes added to the recipe modules as aliases for backward compatibility, but will be removed in future versions. Prefer using the renamed classes.
- `InputOverrideConfig` renamed to `<Recipe>OverrideConfig`
- `OverrideConfig` renamed to `Normalised<Recipe>OverrideConfig`
- Input config classes like `<Recipe>InputConfig` renamed to `<Recipe>Config`
- Normalised config classes like `<Recipe>Config` renamed to `Normalised<Recipe>Config`
- Changed classes:
- AccountLinking `InputOverrideConfig` -> `AccountLinkingOverrideConfig`
- Dashboard `InputOverrideConfig` -> `DashboardOverrideConfig`
- EmailPassword
- `InputOverrideConfig` -> `EmailPasswordOverrideConfig`
- `exceptions` export removed from `__init__`, import the `exceptions` module directly
- EmailVerification
- `InputOverrideConfig` -> `EmailVerificationOverrideConfig`
- `exception` export removed from `__init__`, import the `exceptions` module directly
- JWT `OverrideConfig` -> `JWTOverrideConfig`
- MultiFactorAuth `OverrideConfig` -> `MultiFactorAuthOverrideConfig`
- Multitenancy
- `InputOverrideConfig` -> `MultitenancyOverrideConfig`
- `exceptions` export removed from `__init__`, import the `exceptions` module directly
- OAuth2Provider
- `InputOverrideConfig` -> `OAuth2ProviderOverrideConfig`
- `exceptions` export removed from `__init__`, import the `exceptions` module directly
- OpenId `InputOverrideConfig` -> `OpenIdOverrideConfig`
- Passwordless `InputOverrideConfig` -> `PasswordlessOverrideConfig`
- Session
- `InputOverrideConfig` -> `SessionOverrideConfig`
- `exceptions` export removed from `__init__`, import the `exceptions` module directly
- ThirdParty
- `InputOverrideConfig` -> `ThirdPartyOverrideConfig`
- `exceptions` export removed from `__init__`, import the `exceptions` module directly
- TOTP `OverrideConfig` -> `TOTPOverrideConfig`
- UserMetadata `InputOverrideConfig` -> `UserMetadataOverrideConfig`
- UserRoles `InputOverrideConfig` -> `UserRolesOverrideConfig`

## [0.30.1] - 2025-07-21
- Adds missing register credential endpoint to the Webauthn recipe

Expand Down
3 changes: 2 additions & 1 deletion dev-requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,12 @@ fastapi==0.115.5
Flask==3.0.3
flask-cors==5.0.0
nest-asyncio==1.6.0
packaging==25.0
pdoc3==0.11.0
pre-commit==3.5.0
pyfakefs==5.7.4
pylint==3.2.7
pyright==1.1.393
pyright==1.1.402
python-dotenv==1.0.1
pytest==8.3.3
pytest-asyncio==0.24.0
Expand Down
7 changes: 6 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@ line-length = 88 # Match Black's formatting
src = ["supertokens_python"]

[tool.ruff.lint]
extend-select = ["I"] # enable import sorting
extend-select = [
"I", # enable import sorting
"RUF022", # Sort __all__ exports
]

[tool.ruff.format]
quote-style = "double" # Default
Expand All @@ -18,3 +21,5 @@ include = ["supertokens_python/", "tests/", "examples/"]
addopts = " -v -p no:warnings"
python_paths = "."
xfail_strict = true
# Removes requirement to use `@mark.asyncio` on async tests
asyncio_mode = "auto"
41 changes: 21 additions & 20 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,28 +61,28 @@
}

exclude_list = [
"tests",
"examples",
"hooks",
".gitignore",
".circleci",
".git",
".github",
".gitignore",
".pylintrc",
"Makefile",
"addDevTag",
"addReleaseTag",
"frontendDriverInterfaceSupported.json",
"coreDriverInterfaceSupported.json",
".github",
".circleci",
"html",
"pyrightconfig.json",
"Makefile",
".pylintrc",
"dev-requirements.txt",
"docs-templates",
"examples",
"frontendDriverInterfaceSupported.json",
"hooks",
"html",
"pyrightconfig.json",
"tests",
]

setup(
name="supertokens_python",
version="0.30.1",
version="0.31.0",
author="SuperTokens",
license="Apache 2.0",
author_email="[email protected]",
Expand Down Expand Up @@ -112,22 +112,23 @@
],
keywords="",
install_requires=[
"Deprecated<1.3.0",
# [crypto] ensures that it installs the `cryptography` library as well
# based on constraints specified in https://github.com/jpadilla/pyjwt/blob/master/setup.cfg#L50
"PyJWT[crypto]>=2.5.0,<3.0.0",
"httpx>=0.15.0,<1.0.0",
"pycryptodome<3.21.0",
"tldextract<6.0.0",
"aiosmtplib>=1.1.6,<4.0.0",
"asgiref>=3.4.1,<4",
"typing_extensions>=4.1.1,<5.0.0",
"Deprecated<1.3.0",
"httpx>=0.15.0,<1.0.0",
"packaging>=25.0,<26.0",
"phonenumbers<9",
"twilio<10",
"aiosmtplib>=1.1.6,<4.0.0",
"pkce<1.1.0",
"pycryptodome<3.21.0",
"pydantic>=2.10.6,<3.0.0",
"pyotp<3",
"python-dateutil<3",
"pydantic>=2.10.6,<3.0.0",
"tldextract<6.0.0",
"twilio<10",
"typing_extensions>=4.1.1,<5.0.0",
],
python_requires=">=3.8",
include_package_data=True,
Expand Down
63 changes: 53 additions & 10 deletions supertokens_python/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,38 +12,61 @@
# License for the specific language governing permissions and limitations
# under the License.

from typing import Any, Callable, Dict, List, Optional
from typing import Any, Dict, List, Optional

from typing_extensions import Literal

from supertokens_python.framework.request import BaseRequest
from supertokens_python.recipe_module import RecipeModule
from supertokens_python.types import RecipeUserId

from . import supertokens
from .recipe_module import RecipeModule
from .plugins import LoadPluginsResponse
from .supertokens import (
AppInfo,
InputAppInfo,
RecipeInit,
Supertokens,
SupertokensConfig,
SupertokensExperimentalConfig,
SupertokensInputConfig,
SupertokensPublicConfig,
)

InputAppInfo = supertokens.InputAppInfo
Supertokens = supertokens.Supertokens
SupertokensConfig = supertokens.SupertokensConfig
AppInfo = supertokens.AppInfo
# Some Pydantic models need a rebuild to resolve ForwardRefs
# Referencing imports here to prevent lint errors.
# Caveat: These will be available for import from this module directly.
RecipeModule # type: ignore

# LoadPluginsResponse -> SupertokensPublicConfig
LoadPluginsResponse.model_rebuild()
# SupertokensInputConfig -> RecipeModule
SupertokensInputConfig.model_rebuild()


def init(
app_info: InputAppInfo,
framework: Literal["fastapi", "flask", "django"],
supertokens_config: SupertokensConfig,
recipe_list: List[Callable[[supertokens.AppInfo], RecipeModule]],
recipe_list: List[RecipeInit],
mode: Optional[Literal["asgi", "wsgi"]] = None,
telemetry: Optional[bool] = None,
debug: Optional[bool] = None,
experimental: Optional[SupertokensExperimentalConfig] = None,
):
return Supertokens.init(
app_info, framework, supertokens_config, recipe_list, mode, telemetry, debug
app_info,
framework,
supertokens_config,
recipe_list,
mode,
telemetry,
debug,
experimental=experimental,
)


def get_all_cors_headers() -> List[str]:
return supertokens.Supertokens.get_instance().get_all_cors_headers()
return Supertokens.get_instance().get_all_cors_headers()


def get_request_from_user_context(
Expand All @@ -54,3 +77,23 @@ def get_request_from_user_context(

def convert_to_recipe_user_id(user_id: str) -> RecipeUserId:
return RecipeUserId(user_id)


is_recipe_initialized = Supertokens.is_recipe_initialized


__all__ = [
"AppInfo",
"InputAppInfo",
"RecipeInit",
"RecipeUserId",
"Supertokens",
"SupertokensConfig",
"SupertokensExperimentalConfig",
"SupertokensPublicConfig",
"convert_to_recipe_user_id",
"get_all_cors_headers",
"get_request_from_user_context",
"init",
"is_recipe_initialized",
]
2 changes: 1 addition & 1 deletion supertokens_python/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
from __future__ import annotations

SUPPORTED_CDI_VERSIONS = ["5.3"]
VERSION = "0.30.1"
VERSION = "0.31.0"
TELEMETRY = "/telemetry"
USER_COUNT = "/users/count"
USER_DELETE = "/user/remove"
Expand Down
4 changes: 4 additions & 0 deletions supertokens_python/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,7 @@ class GeneralError(SuperTokensError):

class BadInputError(SuperTokensError):
pass


class PluginError(SuperTokensError):
pass
Loading
Loading