Skip to content

feat: plugins #594

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

Draft
wants to merge 61 commits into
base: 0.29
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
61 commits
Select commit Hold shift + click to select a range
1c46102
feat: port config types, setup skeleton
namsnath Apr 23, 2025
1dc46f4
update: uses generic type for overrides
namsnath Apr 23, 2025
75dd645
feat: adds register_options types, uses dataclasses
namsnath Apr 23, 2025
fd66bd3
fix: cyclic import
namsnath Apr 24, 2025
d1b5ac2
refactor: use dataclasses
namsnath Apr 24, 2025
9d205c3
feat: adds remaining `RecipeInterface` methods/types
namsnath Apr 25, 2025
7b633bb
feat: adds `ApiInterface` methods/types
namsnath Apr 25, 2025
c7902ca
fix: inconsistencies
namsnath Apr 26, 2025
e0ecd16
feat: adds recipe implementation
namsnath Apr 30, 2025
3751c41
update: `LinkingToSessionUserFailedError` types
namsnath Apr 30, 2025
a786a19
update: fixes types for kwargs
namsnath May 1, 2025
5a86342
update: rename base dataclass, fix types
namsnath May 1, 2025
ffe3a71
refactor: Switch to Pydantic models instead of dataclass
namsnath May 2, 2025
2b74292
feat: implements `sign_in_post`
namsnath May 5, 2025
e91df08
update: adds pydantic dependency
namsnath May 5, 2025
bea7436
feat: APIImplementation, EmailDelivery
namsnath May 6, 2025
409b0e0
feat: adds recipe and api functions
namsnath May 8, 2025
5e2a7ca
update: update auth-react django server
namsnath May 9, 2025
173ce7d
update: fix circular imports from auth_utils/webauthn
namsnath May 9, 2025
add6404
fix: return `WebauthnRecipe` instance from `init`
namsnath May 9, 2025
9a282e2
update: remove __future__ annotations from webauthn
namsnath May 9, 2025
b126c08
fix: cyclic imports
namsnath May 12, 2025
5ec1d3d
fix: input config type
namsnath May 12, 2025
cd80d8f
feat: adds syncify decorator
namsnath May 12, 2025
64532a6
feat: adds function wrappers
namsnath May 13, 2025
a0ae4e8
fix: user webauthn types, update querier cdi version
namsnath May 14, 2025
8ad9a1b
fix: various issues
namsnath May 15, 2025
1ec9e9b
update: creates models in api functions
namsnath May 15, 2025
aade01b
update: adds missing import
namsnath May 15, 2025
e2b91e5
update: defaults for options APIs
namsnath May 15, 2025
4c89dc8
feat: adds webauthn support to backend sdk testing server
namsnath May 15, 2025
5383fda
update: removes model validation from server endpoints
namsnath May 15, 2025
f6e5193
update: removes model validation from api endpoints
namsnath May 18, 2025
60a360e
update: remove dataclasses_json refs
namsnath May 19, 2025
61fe34c
update: return error if credential validation fails in API methods
namsnath May 20, 2025
3289e89
fix: null checks for email
namsnath May 20, 2025
eaaf5f2
fix: adds type ignores
namsnath May 20, 2025
24da5f7
fix: recover token flow not being able to find email
namsnath May 20, 2025
f66f5fb
feat: auth-react servers
namsnath May 20, 2025
080ad61
fix: backend-sdk mock parsing
namsnath May 21, 2025
6bb6927
fix: failing unit-test
namsnath May 21, 2025
5c98f01
update: add backend-sdk server logs
namsnath May 22, 2025
d6590e3
fix: failing unit test
namsnath May 22, 2025
74da8c5
update: add defaults for webauthn in `User`
namsnath May 22, 2025
468d4f5
refactor: review comments
namsnath May 26, 2025
ad03588
update: match get_origin types with AppInfo implementation
namsnath May 27, 2025
36e4478
feat: adds UTs for Webauthn config
namsnath May 27, 2025
3186b11
update: changelog
namsnath May 28, 2025
1841adb
fix: add missing test function
namsnath May 28, 2025
9e6abae
fix: add steps to lint-code workflow
namsnath May 28, 2025
23e4729
fix: types
namsnath May 28, 2025
3a19cb5
feat: adds basic plugin functionality
namsnath Jun 5, 2025
94cde2b
feat: make plugins functional
namsnath Jun 6, 2025
c1265cc
update: change plugin evaluation order
namsnath Jun 6, 2025
06dfa51
update: move plugin logic to function, add config override support
namsnath Jun 10, 2025
d289908
update: cleanup load_plugins
namsnath Jun 10, 2025
a03cd24
test: st config overrides, route handlers
namsnath Jun 10, 2025
05034c9
refactor: move classes to new file
namsnath Jun 10, 2025
1fa112a
update: import order
namsnath Jun 10, 2025
d2c9f3f
update: adds tests
namsnath Jun 16, 2025
bc23f0f
update: adds init tests
namsnath Jun 18, 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
3 changes: 2 additions & 1 deletion .github/workflows/backend-sdk-testing.yml
Original file line number Diff line number Diff line change
Expand Up @@ -82,10 +82,11 @@ jobs:
run: |
source venv/bin/activate
docker compose up --build --wait
python3 tests/test-server/app.py &
python3 tests/test-server/app.py &> python.log &

- uses: supertokens/backend-sdk-testing-action@main
with:
version: ${{ matrix.fdi-version }}
check-name-suffix: '[CDI=${{ matrix.cdi-version }}][Core=${{ steps.versions.outputs.coreVersionXy }}][FDI=${{ matrix.fdi-version }}][Py=${{ matrix.py-version }}][Node=${{ matrix.node-version }}]'
path: backend-sdk-testing
app-server-logs: ${{ github.workspace }}/supertokens-python/python.log
5 changes: 5 additions & 0 deletions .github/workflows/lint-code.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@ jobs:
outputs:
pyVersions: '["3.8", "3.9", "3.10", "3.11", "3.12", "3.13"]'

steps:
# Required to avoid errors in runs due to no steps
- name: Placeholder
run: echo "Placeholder"

lint-format:
name: Check linting and formatting
runs-on: ubuntu-latest
Expand Down
39 changes: 39 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,45 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [unreleased]

## [0.30.0] - 2025-05-27
### Adds Webauthn (Passkeys) support
- Adds Webauthn recipe with support for:
- Registration, sign-in, and credential verification flows
- Account recovery
- Adds new API endpoints for WebAuthn operations:
- GET `/api/webauthn/email/exists` - Check if email exists in system
- POST `/api/webauthn/options/register` - Handle registration options
- POST `/api/webauthn/options/signin` - Handle sign-in options
- POST `/api/webauthn/signin` - Handle WebAuthn sign-in
- POST `/api/webauthn/signup` - Handle WebAuthn sign-up
- POST `/api/user/webauthn/reset` - Handle account recovery
- POST `/api/user/webauthn/reset/token` - Generate recovery tokens
- Adds WebAuthn support to account linking functionality:
- Support for linking users based on WebAuthn `credential_id`
- Updates `AccountInfo` type to `AccountInfoInput` with WebAuthn fields
- Adds `has_same_webauthn_info_as` method for credential comparison
- Adds FDI support for version `4.1`
- Recipe functions are directly importable from the Webauthn recipe module
- ```python
from supertokens_python.recipe.webauthn import sign_in

await sign_in(...) # Async
sign_in.sync(...) # Sync
```

### Breaking Changes
- Updates supported CDI version from `5.2` to `5.3`
- Changes `AccountInfo` to `AccountInfoInput` in various methods
- This is required to allow querying by a single Webauthn `credential_id`, while the Webauthn login method contains an array of `credential_ids`
- Affected functions:
- `supertokens_python.asyncio.list_users_by_account_info`
- `supertokens_python.syncio.list_users_by_account_info`
- `supertokens_python.recipe.accountlinking.interface.RecipeInterface.list_users_by_account_info`
- `supertokens_python.recipe.accountlinking.recipe_implementation.RecipeImplementation.list_users_by_account_info`
- `supertokens_python.recipe.passwordless.api.implementation.get_passwordless_user_by_account_info`
- `supertokens_python.recipe.passwordless.api.implementation.get_passwordless_user_by_account_info`


## [0.29.2] - 2025-05-19
- Fixes cookies being set without expiry in Django
- Reverts timezone change from 0.28.0 and uses GMT
Expand Down
2 changes: 1 addition & 1 deletion coreDriverInterfaceSupported.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"_comment": "contains a list of core-driver interfaces branch names that this core supports",
"versions": [
"5.2"
"5.3"
]
}
1 change: 1 addition & 0 deletions dev-requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,5 @@ pyyaml==6.0.2
requests-mock==1.12.1
respx>=0.13.0, <1.0.0
uvicorn==0.32.0
wasmtime==25.0.0
-e .
3 changes: 2 additions & 1 deletion frontendDriverInterfaceSupported.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
"2.0",
"3.0",
"3.1",
"4.0"
"4.0",
"4.1"
]
}
2 changes: 2 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,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"
3 changes: 2 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@

setup(
name="supertokens_python",
version="0.29.2",
version="0.30.0",
author="SuperTokens",
license="Apache 2.0",
author_email="[email protected]",
Expand Down Expand Up @@ -127,6 +127,7 @@
"pkce<1.1.0",
"pyotp<3",
"python-dateutil<3",
"pydantic>=2.10.6,<3.0.0",
],
python_requires=">=3.8",
include_package_data=True,
Expand Down
18 changes: 14 additions & 4 deletions supertokens_python/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,33 +12,43 @@
# 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

InputAppInfo = supertokens.InputAppInfo
Supertokens = supertokens.Supertokens
SupertokensConfig = supertokens.SupertokensConfig
AppInfo = supertokens.AppInfo
SupertokensExperimentalConfig = supertokens.SupertokensExperimentalConfig
RecipeModule = RecipeModule


def init(
app_info: InputAppInfo,
framework: Literal["fastapi", "flask", "django"],
supertokens_config: SupertokensConfig,
recipe_list: List[Callable[[supertokens.AppInfo], RecipeModule]],
recipe_list: List[supertokens.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,
)


Expand Down
36 changes: 35 additions & 1 deletion supertokens_python/async_to_sync_wrapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,20 @@
# under the License.

import asyncio
from functools import update_wrapper
from os import getenv
from typing import Any, Coroutine, TypeVar
from typing import (
Any,
Callable,
Coroutine,
Generic,
TypeVar,
)

from typing_extensions import ParamSpec

Param = ParamSpec("Param")
RetType = TypeVar("RetType", covariant=True)

_T = TypeVar("_T")

Expand Down Expand Up @@ -43,3 +55,25 @@ def create_or_get_event_loop() -> asyncio.AbstractEventLoop:
def sync(co: Coroutine[Any, Any, _T]) -> _T:
loop = create_or_get_event_loop()
return loop.run_until_complete(co)


class syncify(Generic[Param, RetType]):
"""
Decorator to allow async functions to be executed synchronously
using a `sync` attribute.
"""

def __init__(self, func: Callable[Param, Coroutine[Any, Any, RetType]]):
update_wrapper(self, func)
self.func = func

def __call__(
self, *args: Param.args, **kwargs: Param.kwargs
) -> Coroutine[Any, Any, RetType]:
return self.func(*args, **kwargs)

def sync(self, *args: Param.args, **kwargs: Param.kwargs) -> RetType:
"""
Synchronous version of the decorated function.
"""
return sync(self.func(*args, **kwargs))
5 changes: 3 additions & 2 deletions supertokens_python/asyncio/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@
)
from supertokens_python.recipe.accountlinking.interfaces import GetUsersResult
from supertokens_python.recipe.accountlinking.recipe import AccountLinkingRecipe
from supertokens_python.types import AccountInfo, User
from supertokens_python.types import User
from supertokens_python.types.base import AccountInfoInput


async def get_users_oldest_first(
Expand Down Expand Up @@ -159,7 +160,7 @@ async def update_or_delete_user_id_mapping_info(

async def list_users_by_account_info(
tenant_id: str,
account_info: AccountInfo,
account_info: AccountInfoInput,
do_union_of_account_info: bool = False,
user_context: Optional[Dict[str, Any]] = None,
) -> List[User]:
Expand Down
38 changes: 12 additions & 26 deletions supertokens_python/auth_utils.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from typing import Any, Awaitable, Callable, Dict, List, Optional, Union
from typing import TYPE_CHECKING, Any, Awaitable, Callable, Dict, List, Optional, Union

from typing_extensions import Literal

Expand Down Expand Up @@ -31,37 +31,18 @@
from supertokens_python.recipe.session.interfaces import SessionContainer
from supertokens_python.recipe.thirdparty.types import ThirdPartyInfo
from supertokens_python.types import (
AccountInfo,
LoginMethod,
RecipeUserId,
User,
)
from supertokens_python.types.auth_utils import LinkingToSessionUserFailedError
from supertokens_python.types.base import AccountInfoInput
from supertokens_python.utils import log_debug_message

from .asyncio import get_user


class LinkingToSessionUserFailedError:
status: Literal["LINKING_TO_SESSION_USER_FAILED"] = "LINKING_TO_SESSION_USER_FAILED"
reason: Literal[
"EMAIL_VERIFICATION_REQUIRED",
"RECIPE_USER_ID_ALREADY_LINKED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR",
"ACCOUNT_INFO_ALREADY_ASSOCIATED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR",
"SESSION_USER_ACCOUNT_INFO_ALREADY_ASSOCIATED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR",
"INPUT_USER_IS_NOT_A_PRIMARY_USER",
]

def __init__(
self,
reason: Literal[
"EMAIL_VERIFICATION_REQUIRED",
"RECIPE_USER_ID_ALREADY_LINKED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR",
"ACCOUNT_INFO_ALREADY_ASSOCIATED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR",
"SESSION_USER_ACCOUNT_INFO_ALREADY_ASSOCIATED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR",
"INPUT_USER_IS_NOT_A_PRIMARY_USER",
],
):
self.reason = reason
if TYPE_CHECKING:
from supertokens_python.recipe.webauthn.types.base import WebauthnInfoInput


class OkResponse:
Expand Down Expand Up @@ -290,6 +271,7 @@ async def get_authenticating_user_and_add_to_current_tenant_if_required(
session: Optional[SessionContainer],
check_credentials_on_tenant: Callable[[str], Awaitable[bool]],
user_context: Dict[str, Any],
webauthn: Optional["WebauthnInfoInput"] = None,
) -> Optional[AuthenticatingUserInfo]:
i = 0
while i < 300:
Expand All @@ -303,8 +285,11 @@ async def get_authenticating_user_and_add_to_current_tenant_if_required(
)
existing_users = await AccountLinkingRecipe.get_instance().recipe_implementation.list_users_by_account_info(
tenant_id=tenant_id,
account_info=AccountInfo(
email=email, phone_number=phone_number, third_party=third_party
account_info=AccountInfoInput(
email=email,
phone_number=phone_number,
third_party=third_party,
webauthn=webauthn,
),
do_union_of_account_info=True,
user_context=user_context,
Expand All @@ -324,6 +309,7 @@ async def get_authenticating_user_and_add_to_current_tenant_if_required(
(email is not None and lm.has_same_email_as(email))
or lm.has_same_phone_number_as(phone_number)
or lm.has_same_third_party_info_as(third_party)
or lm.has_same_webauthn_info_as(webauthn)
)
),
None,
Expand Down
4 changes: 2 additions & 2 deletions supertokens_python/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@

from __future__ import annotations

SUPPORTED_CDI_VERSIONS = ["5.2"]
VERSION = "0.29.2"
SUPPORTED_CDI_VERSIONS = ["5.3"]
VERSION = "0.30.0"
TELEMETRY = "/telemetry"
USER_COUNT = "/users/count"
USER_DELETE = "/user/remove"
Expand Down
Loading
Loading