All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
- Fixes dependency for docs build
- Not supporting Python 3.7
- Updating dependencies for vulnerability fixes
- Fixes issues with dashboard - userroles and tenants
- Fixes
LoginMethod
to json conversion to not includenull
as values - Adds
to_json
method toTenantConfig
- Added
RecipeUserId
and a genericUser
class - Added
get_user
,list_users_by_account_info
,convert_to_recipe_user_id
to the main exports - Added account-linking recipe
- Enable smooth switching between
use_dynamic_access_token_signing_key
settings by allowing refresh calls to change the signing key type of a session - Added a core call cache that should reduce traffic to your SuperTokens core instances
- Refactored sign in/up API codes to reduce code duplication
- Added MFA related information to dashboard APIs
- Added a cache to reduce the number of requests made to the core. This can be disabled using the
disable_core_call_cache: true
, in the config. - Added new function:
check_code
to Passwordless and ThirdPartyPasswordless recipes - Added new function:
verify_credentials
to EmailPassword and ThirdPartyEmailPassword recipes - Added the
MultiFactorAuth
andTOTP
recipes.
- Now only supporting CDI 5.1. Compatible with core version >= 9.1
- Added new support codes to sign in/up APIs. This means that there are new possible values coming from the default implementation for the
reason
strings ofSIGN_IN_NOT_ALLOWED
,SIGN_UP_NOT_ALLOWED
andSIGN_IN_UP_NOT_ALLOWED
responses. - Removed the recipe specific
User
type, now all functions are using the new genericUser
type.- Check here for more information.
- The
build
function and thefetch_value
callback of session claims now take a newrecipe_user_id
param.- This affects built-in claims:
EmailVerificationClaim
,UserRoleClaim
,PermissionClaim
,AllowedDomainsClaim
. - This will affect all custom claims as well built on our base classes.
- This affects built-in claims:
- Now ignoring protected props in the payload in
create_new_session
andcreate_new_session_without_request_response
created_new_user
has been renamed tocreated_new_recipe_user
in sign up related APIs and functionsSession
recipe:- The sign out API new returns a 401 instead of 200 in case the input access token has expired or is missing.
EmailPassword
:- removed
get_user_by_id
,get_user_by_email
. You should usesupertokens.get_user
, andsupertokens.list_users_by_account_info
instead - added
consume_password_reset_token
. This function allows the consumption of the reset password token without changing the password. It will return OK if the token was valid. - added an overrideable
create_new_recipe_user
function that is called during sign up and password reset flow (in case a new email password user is being created on the fly). This is mostly for internal use. recipe_user_id
is added to the input ofget_content
of the email delivery configemail
was added to the input ofcreate_reset_password_token
,send_reset_password_email
,create_reset_password_link
update_email_or_password
:- now takes
recipe_user_id
instead ofuser_id
- can return the new
EMAIL_CHANGE_NOT_ALLOWED_ERROR
status
- now takes
reset_password_using_token
:- removed from the recipe interface, making it no longer overrideable (directly)
- the related function in the index files now call
consume_password_reset_token
andupdate_email_or_password
- any necessary behaviour changes can be achieved by overriding those two function instead
sign_in_post
:- can return status
SIGN_IN_NOT_ALLOWED
- can return status
sign_up_post
:- can return status
SIGN_UP_NOT_ALLOWED
- can return status
generate_password_reset_token_post
:- can now return
PASSWORD_RESET_NOT_ALLOWED
- can now return
password_reset_post
:- now returns the
user
and theemail
whose password was reset - can now return
PASSWORD_POLICY_VIOLATED_ERROR
- now returns the
- Changed the signature of the following overrideable functions:
sign_up
- Takes a new (optional)
session
parameter - Can now return with
status: "LINKING_TO_SESSION_USER_FAILED"
- returns new
recipe_user_id
prop in thestatus: OK
case
- Takes a new (optional)
sign_in
- Takes a new (optional)
session
parameter - Can now return with
status: "LINKING_TO_SESSION_USER_FAILED"
- returns new
recipe_user_id
prop in thestatus: OK
case
- Takes a new (optional)
- Changed the signature of overrideable APIs, adding a new (optional) session parameter:
sign_in_post
sign_up_post
- Changed the signature of functions:
sign_up
- Takes a new (optional)
session
parameter - Can now return with
status: "LINKING_TO_SESSION_USER_FAILED"
- Takes a new (optional)
sign_in
- Takes a new (optional)
session
parameter - Can now return with `status: "LINKING_TO_SESSION_USER_FAILED"
- Takes a new (optional)
- removed
EmailVerification
:create_email_verification_token
,create_email_verification_link
,is_email_verified
,revoke_email_verification_tokens
,unverify_email
:- now takes
recipe_user_id
instead ofuser_id
- now takes
send_email_verification_email
:- now takes an additional
recipe_user_id
parameter
- now takes an additional
verify_email_using_token
:- now takes a new
attempt_account_linking
parameter - returns the
recipe_user_id
instead ofid
- now takes a new
send_email
now requires a newrecipe_user_id
as part of the user infoget_email_for_user_id
config option was renamed toget_email_for_recipe_user_id
verify_email_post
,generate_email_verify_token_post
: returns an optionalnew_session
in case the current user session needs to be updated
Multitenancy
:associate_user_to_tenant
can now returnASSOCIATION_NOT_ALLOWED_ERROR
associate_user_to_tenant
anddisassociate_user_from_tenant
now takeRecipeUserId
instead of a string user id- Changed the signature of the following functions:
create_or_update_tenant
: Added optionalfirst_factors
andrequired_secondary_factors
parameters.get_tenant
: Addedfirst_factors
andrequired_secondary_factors
to the return typelist_all_tenants
: Addedfirst_factors
andrequired_secondary_factors
to the returned tenants
- Changed the signature of the following overrideable functions:
create_or_update_tenant
: Now gets optionalfirst_factors
andrequired_secondary_factors
in the input.get_tenant
: Addedfirst_factors
andrequired_secondary_factors
to the return typelist_all_tenants
: Addedfirst_factors
andrequired_secondary_factors
to the returned tenants
- Changed the signature of the overrideable apis:
login_methods_get
: Now returnsfirst_factors
Passwordless
:- removed
get_user_by_id
,get_user_by_email
,get_user_by_phone_number
update_user
:- now takes
recipe_user_id
instead ofuser_id
- can return
"EMAIL_CHANGE_NOT_ALLOWED_ERROR
andPHONE_NUMBER_CHANGE_NOT_ALLOWED_ERROR
statuses
- now takes
create_code_post
andconsume_code_post
can now returnSIGN_IN_UP_NOT_ALLOWED
revoke_code
(and the related overrideable func) can now be called with eitherpre_auth_session_id
orcode_id
instead of onlycode_id
.- Added new email and sms type for MFA
- Changed the signature of the following functions:
sign_in_up
,create_code
: Takes a new (optional)session
parameterconsume_code
:- Takes a new (optional)
session
parameter - Can now return with
status: "LINKING_TO_SESSION_USER_FAILED"
- It now also returns
consumed_device
if the code was successfully consumed
- Takes a new (optional)
- Changed the signature of the following overrideable functions:
create_code
: Takes a new (optional)session
parameterconsume_code
:- Takes a new (optional)
session
parameter - Can now return with
status: "LINKING_TO_SESSION_USER_FAILED"
- It now also returns
consumed_device
if the code was successfully consumed
- Takes a new (optional)
- Changed the signature of overrideable APIs, adding a new (optional) session parameter:
create_code_post
resend_code_post
consume_code_post
- removed
Session
:- access tokens and session objects now contain the recipe user id
- Support for new access token version
recipe_user_id
is now added to the payload of theTOKEN_THEFT_DETECTED
errorcreate_new_session
: now takesrecipe_user_id
instead ofuser_id
- Removed
validate_claims_in_jwt_payload
revoke_all_sessions_for_user
now takes an optionalrevoke_sessions_for_linked_accounts
paramget_all_session_handles_for_user
now takes an optionalfetch_sessions_for_all_linked_accounts
paramregenerate_access_token
return value now includesrecipe_user_id
get_global_claim_validators
andvalidate_claims
now get a newrecipe_user_id
param- Added
get_recipe_user_id
to the session class
- Session claims:
- The
build
function and thefetch_value
callback of session claims now take a newcurrent_payload
param.- This affects built-in claims:
EmailVerificationClaim
,UserRoleClaim
,PermissionClaim
,AllowedDomainsClaim
. - This will affect all custom claims as well built on our base classes.
- This affects built-in claims:
- The
ThirdParty
:- Removed
get_user_by_third_party_info
,get_users_by_email
,get_user_by_id
sign_in_up_post
can now returnSIGN_IN_UP_NOT_ALLOWED
- Changed the signature of the following functions:
manually_create_or_update_user
:- Takes a new (optional)
session
parameter - Can now return with
status: "LINKING_TO_SESSION_USER_FAILED"
- Takes a new (optional)
- Changed the signature of the following overrideable functions:
sign_in_up
:- gets a new
is_verified
param - can return new status:
SIGN_IN_UP_NOT_ALLOWED
- Takes a new (optional)
session
parameter - Can now return with
status: "LINKING_TO_SESSION_USER_FAILED"
- gets a new
manually_create_or_update_user
- gets a new
is_verified
param - can return new statuses:
EMAIL_CHANGE_NOT_ALLOWED_ERROR
,SIGN_IN_UP_NOT_ALLOWED
- Takes a new (optional)
session
parameter - Can now return with
status: "LINKING_TO_SESSION_USER_FAILED"
- gets a new
- Changed the signature of overrideable APIs, adding a new (optional) session parameter:
sign_in_up_post
- Removed
With this release, we are introducing a new AccountLinking recipe, this will let you:
- link accounts automatically,
- implement manual account linking flows.
Check our guide for more information.
In this release, we've removed the recipes specific user types and instead introduced a new User
class to support the "Primary user" concept introduced by account linking
- The new
User
class now provides the same interface for all recipes. - It contains an
is_primary
field that you can use to differentiate between primary and recipe users - The
login_methods
array contains objects that covers all props of the old (recipe specific) user types, with the exception of the id. Please check the migration section below to get the exact mapping between old and new props. - Non-primary users:
- The
login_methods
array should contain exactly 1 element. user.id
will be the same asuser.login_methods[0].recipe_user_id.get_as_string()
.user.id
will change if it is linked to another user.- They can become a primary user if, and only if there are no other primary users with the same email, third party info or phone number as this user across all the tenants that this user is a part of.
- The
- Primary users
- The
login_methods
array can have 1 or more elements, each corresponding to a single recipe user. user.id
will not change even if other users are linked to it.- Other non-primary users can be linked to it. The user ID of the linked accounts will now be the primary users ID.
- The
- Check here for more information about differences between primary and recipe users.
Because of account linking we've introduced a new Primary user concept (see above). In most cases, you should only use the primary user id (user.id
or session.get_user_id()
) if you are associating data to users. Still, in some cases you need to specifically refer to a login method, which is covered by the new RecipeUserId
class:
- You can get it:
- From a session by:
session.get_recipe_user_id()
. - By finding the appropriate entry in the
login_methods
array of aUser
object (see above):user.login_methods[0].recipe_user_id
.
- From a session by:
- It wraps a simple string value that you can get by calling
recipe_user_id.get_as_string()
. - We've introduced it to differentiate between primary and recipe user ids in our APIs on a type level.
- Check here for more information.
We've added a generic User
class instead of the old recipe specific ones. The mapping of old props to new in case you are not using account-linking:
user.id
staysuser.id
(oruser.login_methods[0].recipe_user_id
in case you needRecipeUserId
)user.email
becomesuser.emails[0]
user.phone_number
becomesuser.phone_numbers[0]
user.third_party
becomesuser.third_party[0]
user.time_joined
is stilluser.time_joined
user.tenant_ids
is stilluser.tenant_ids
Some functions now require you to pass a RecipeUserId
instead of a string user id. If you are using our auth recipes, you can find the recipeUserId as: user.login_methods[0].recipe_user_id
(you'll need to worry about selecting the right login method after enabling account linking). Alternatively, if you already have a string user id you can convert it to a RecipeUserId
using supertokens_python.convert_to_recipe_user_id(userIdString)
- In the passwordless consumeCode / social login signinup APIs, you can check if a user signed up by:
# Here res refers to the result the function/api functions mentioned above.
is_new_user = res.created_new_recipe_user and len(res.user.login_methods) == 1
- In the emailpassword sign up API, you can check if a user signed up by:
is_new_user = len(res.user.login_methods) == 1
- We recommend that you check if the email change of a user is allowed, before calling the update function
- Check here for more information
from fastapi import Depends
from supertokens_python.recipe.session.framework.fastapi import verify_session
from supertokens_python.recipe.session import SessionContainer
from supertokens_python.recipe.accountlinking.asyncio import is_email_change_allowed
from supertokens_python.recipe.emailpassword.asyncio import update_email_or_password
# ...
@app.post("/change-email")
async def change_email(req: ChangeEmailBody, session: SessionContainer = Depends(verify_session())):
email = req.email
if not await is_email_change_allowed(session.get_recipe_user_id(), email, False):
# this can come here if you have enabled the account linking feature, and
# if there is a security risk in changing this user's email.
pass
# Update the email
await update_email_or_password(
session.get_recipe_user_id(),
email,
)
# ...
We've added a new optional session
parameter to many function calls. In all cases, these have been added as the last parameter before user_context
, so this should only affect you if you are using that. You only need to pass a session as a parameter if you are using account linking and want to try and link the user signing in/up to the session user.
You can get the necessary session object using verify_session
in an API call.
Here we use the example of EmailPassword.sign_in
but this fits other functions with changed signatures.
Before:
from supertokens_python.recipe.emailpassword.asyncio import sign_in
sign_in_resp = await sign_in("public", "[email protected]", "testpw", { "myContextVar": True })
After:
from supertokens_python.recipe.emailpassword.asyncio import sign_in
sign_in_resp = await sign_in("public", "[email protected]", "testpw", None, { "myContextVar": True })
If you use the user_context
parameter passed to fetch_value
, please update your implementation to account for the new parameter.
Before:
from typing import Any, Dict
from supertokens_python.recipe.session.claims import BooleanClaim
from supertokens_python.types import RecipeUserId
def fetch_value(user_id: str, recipe_user_id: RecipeUserId, tenant_id: str, user_context: Dict[str, Any]) -> bool:
return user_context["claimValue"]
bool_claim = BooleanClaim(
key="asdf",
fetch_value=fetch_value,
)
After:
from typing import Any, Dict
from supertokens_python.recipe.session.claims import BooleanClaim
from supertokens_python.types import RecipeUserId
def fetch_value(user_id: str, recipe_user_id: RecipeUserId, tenant_id: str, current_payload: Dict[str, Any], user_context: Dict[str, Any]) -> bool:
return user_context["claimValue"]
bool_claim = BooleanClaim(
key="asdf",
fetch_value=fetch_value,
)
If you were using the build
function for custom or built-in session claims, you should update the call signature to also pass the new parameter.
Before:
from typing import Any, Dict, Optional
from supertokens_python.recipe import session
from supertokens_python.recipe.session.interfaces import RecipeInterface
from supertokens_python.recipe.userroles import UserRoleClaim
from supertokens_python.types import RecipeUserId
def functions_override(original_implementation: RecipeInterface):
o_create_new_session = original_implementation.create_new_session
async def n_create_new_session(
user_id: str,
recipe_user_id: RecipeUserId,
access_token_payload: Optional[Dict[str, Any]],
session_data_in_database: Optional[Dict[str, Any]],
disable_anti_csrf: Optional[bool],
tenant_id: str,
user_context: Dict[str, Any],
):
access_token_payload = {
**(access_token_payload or {}),
**(await UserRoleClaim.build(user_id, recipe_user_id, tenant_id, user_context))
}
return await o_create_new_session(user_id, recipe_user_id, access_token_payload, session_data_in_database, disable_anti_csrf, tenant_id, user_context)
original_implementation.create_new_session = n_create_new_session
return original_implementation
session.init(override=session.InputOverrideConfig(functions=functions_override))
After:
from typing import Any, Dict, Optional
from supertokens_python.recipe import session
from supertokens_python.recipe.session.interfaces import RecipeInterface
from supertokens_python.recipe.userroles import UserRoleClaim
from supertokens_python.types import RecipeUserId
def functions_override(original_implementation: RecipeInterface):
o_create_new_session = original_implementation.create_new_session
async def n_create_new_session(
user_id: str,
recipe_user_id: RecipeUserId,
access_token_payload: Optional[Dict[str, Any]],
session_data_in_database: Optional[Dict[str, Any]],
disable_anti_csrf: Optional[bool],
tenant_id: str,
user_context: Dict[str, Any],
):
access_token_payload = {
**(access_token_payload or {}),
**(await UserRoleClaim.build(user_id, recipe_user_id, tenant_id, access_token_payload or {}, user_context))
}
return await o_create_new_session(user_id, recipe_user_id, access_token_payload, session_data_in_database, disable_anti_csrf, tenant_id, user_context)
original_implementation.create_new_session = n_create_new_session
return original_implementation
session.init(override=session.InputOverrideConfig(functions=functions_override))
Since now sign in/up APIs and functions can be called with a session (e.g.: during MFA flows), you may need to add an extra check to your overrides to account for that:
# While this example uses Passwordless, all recipes require a very similar change
from typing import Any, Dict, Optional, Union
from supertokens_python.recipe import passwordless
from supertokens_python.recipe.passwordless.interfaces import ConsumeCodeOkResult, RecipeInterface
from supertokens_python.recipe.session import SessionContainer
def functions_override(original_implementation: RecipeInterface):
o_consume_code = original_implementation.consume_code
async def n_consume_code(
pre_auth_session_id: str,
user_input_code: Union[str, None],
device_id: Union[str, None],
link_code: Union[str, None],
session: Optional[SessionContainer],
should_try_linking_with_session_user: Union[bool, None],
tenant_id: str,
user_context: Dict[str, Any],
):
resp = await o_consume_code(
pre_auth_session_id,
user_input_code, device_id,
link_code,
session,
should_try_linking_with_session_user,
tenant_id,
user_context
)
if isinstance(resp, ConsumeCodeOkResult):
if session is None:
if resp.created_new_recipe_user and len(resp.user.login_methods) == 1:
pass # TODO: post sign up logic
else:
pass # TODO: post sign in logic
return resp
original_implementation.consume_code = n_consume_code
return original_implementation
passwordless.init(
passwordless.ContactConfig('EMAIL'),
'USER_INPUT_CODE_AND_MAGIC_LINK',
override=passwordless.InputOverrideConfig(functions=functions_override)
)
Sign in/up APIs and functions will now attempt to link the authenticating user to the session user if a session is available (depending on AccountLinking settings). You can disable this and get the old behaviour by:
Before:
from supertokens_python.recipe import passwordless
# While this example uses Passwordless, all recipes require a very similar change
passwordless.init(
passwordless.ContactConfig('EMAIL'),
'USER_INPUT_CODE_AND_MAGIC_LINK'
)
After:
# While this example uses Passwordless, all recipes require a very similar change
from typing import Any, Dict, Optional, Union
from supertokens_python.recipe import passwordless
from supertokens_python.recipe.passwordless.interfaces import RecipeInterface
from supertokens_python.recipe.session import SessionContainer
def functions_override(original_implementation: RecipeInterface):
o_consume_code = original_implementation.consume_code
async def n_consume_code(
pre_auth_session_id: str,
user_input_code: Union[str, None],
device_id: Union[str, None],
link_code: Union[str, None],
session: Optional[SessionContainer],
should_try_linking_with_session_user: Union[bool, None],
tenant_id: str,
user_context: Dict[str, Any],
):
return await o_consume_code(
pre_auth_session_id,
user_input_code, device_id,
link_code,
None, # do not pass session
should_try_linking_with_session_user,
tenant_id,
user_context
)
original_implementation.consume_code = n_consume_code
return original_implementation
passwordless.init(
passwordless.ContactConfig('EMAIL'),
'USER_INPUT_CODE_AND_MAGIC_LINK',
override=passwordless.InputOverrideConfig(functions=functions_override)
)
- Updates
phonenumbers
andtwilio
to latest versions
- Adds support for form field related improvements by making fields accept any type of values
- Adds support for optional fields to properly optional
To read a value from formFields where id is name
,
Before:
# form_fields should be of type List[FormField]
name: str | None = None
for field in form_fields:
if field.id == 'name':
name = field.value
After:
# form_fields should be of type List[FormField]
name: str | None = None
for field in form_fields:
if field.id == 'name':
# Check type to ensure it's of type string
value_to_consume = field.value
if not isinstance(value_to_consume, str):
# Throw error
raise ValueError('name needs to be a string')
name = str(value_to_consume)
- Makes optional input form fields truly optional instead of just being able to accept
""
.
- Sets time out for httpx client to 30s everywhere. - #516
- Adds test server for
backend-sdk-testing
- Sends
websiteDomain
andapiDomain
to core for telemetry. boxyURL
is no more mandatory input inadditionalConfig
while adding boxy-saml provider in thirdParty.- Adds
jwks_refresh_interval_sec
input toSession.init
to set the default JWKS cache duration. The default is 4 hours.
- Removes the default
max_age_in_seconds
value (previously 300 seconds) in EmailVerification Claim. If the claim value is true andmax_age_in_seconds
is not provided, it will not be refetched. - SDK will no longer add
.well-known/openid-configuration
to theoidc_discovery_endpoint
config in thirdParty providers. If you have specified any customoidc_discovery_endpoint
in the thirdparty.init or added to the core, please make sure to update them to include.well-known/openid-configuration
. - For a non-public tenant, when there are no providers added in the core, the SDK used to fallback to the providers added in the ThirdParty.init. Now, the SDK will not fallback to the providers added in the ThirdParty.init by default. If you require a thirdparty provider to be available for non-public tenants, you can make it available by setting
include_in_non_public_tenants_by_default
for each of the providers. See the migration section below to see how to do this. Note that this only affects non-public tenants when there are no providers added in core.
To make all the providers added in the ThirdParty.init available for non-public tenants by default,
Before:
thirdparty.init(
sign_in_and_up_feature=thirdparty.SignInAndUpFeature(
providers=[
thirdparty.ProviderInput(
config=thirdparty.ProviderConfig(
third_party_id="google",
# rest of the config
)
),
thirdparty.ProviderInput(
config=thirdparty.ProviderConfig(
third_party_id="github",
# rest of the config
)
),
]
)
)
After:
thirdparty.init(
sign_in_and_up_feature=thirdparty.SignInAndUpFeature(
providers=[
thirdparty.ProviderInput(
config=thirdparty.ProviderConfig(
third_party_id="google",
# rest of the config
),
# Add the following line to make this provider available in non-public tenants by default
include_in_non_public_tenants_by_default=True
),
thirdparty.ProviderInput(
config=thirdparty.ProviderConfig(
third_party_id="github",
# rest of the config
),
# Add the following line to make this provider available in non-public tenants by default
include_in_non_public_tenants_by_default=True
),
]
)
)
For each tenant, do the following
-
GET
/appid-<appId>/<tenantId>/recipe/multitenancy/tenant/v2
You should see the thirdParty providers in the response using
response.thirdParty.providers
-
For each config in providers list, if you have
oidcDiscoveryEndpoint
in the config, update it to include.well-known/openid-configuration
at the end.
Here's a sample code snippet to update the oidcDiscoveryEndpoint
:
import supertokens_python.recipe.multitenancy.syncio as multitenancy
def is_custom_provider(third_party_id: str) -> bool:
custom_providers = [
"custom",
# ... all your custom thirdPartyIds
]
return third_party_id in custom_providers
tenants_res = multitenancy.list_all_tenants()
for tenant in tenants_res.tenants:
for provider in tenant.third_party.providers:
if is_custom_provider(provider.third_party_id) and provider.oidc_discovery_endpoint:
provider.oidc_discovery_endpoint = provider.oidc_discovery_endpoint.rstrip("/")
provider.oidc_discovery_endpoint += "/.well-known/openid-configuration"
multitenancy.create_or_update_third_party_config(tenant.tenant_id, provider)
refresh_post
andrefresh_session
now clears all user tokens upon CSRF failures and if no tokens are found. See the latest comment on supertokens/supertokens-node#141 for more details.- Adds
jwks_refresh_interval_sec
config tosession.init
to set the default JWKS cache duration. The default is 4 hours.
- The access token cookie expiry has been changed from 100 years to 1 year due to some browsers capping the maximum expiry at 400 days. No action is needed on your part.
- Remove
user_context
beingNone
check in querier delete function to make it consistent with other non GET functions
- Adds caching per API based on user context.
- Changes general error in querier to normal python error.
- Removed ThirdPartyEmailPassword and ThirdPartyPasswordless recipes. Instead, you should use ThirdParty + EmailPassword or ThirdParty + Passwordless recipes separately in your recipe list.
- Removed
rid
query param from:- email verification links
- passwordless magic links
- password reset links
- If
rid
header is present in an API call, the routing no only only depends on that. If the SDK cannot resolve a request handler based on therid
, request path and method, it will try to resolve a request handler only based on the request path and method (therefore ignoring therid
header). - New API handlers are:
GET /emailpassword/email/exists
=> email password, does email exist API (used to beGET /signup/email/exists
withrid
ofemailpassword
orthirdpartyemailpassword
which is now deprecated)GET /passwordless/email/exists
=> email password, does email exist API (used to beGET /signup/email/exists
withrid
ofpasswordless
orthirdpartypasswordless
which is now deprecated)GET /passwordless/phonenumber/exists
=> email password, does email exist API (used to beGET /signup/phonenumber/exists
which is now deprecated)
- Support for FDI 2.0
-
If you were using
ThirdPartyEmailPassword
, you should now initThirdParty
andEmailPassword
recipes separately. The config for the individual recipes are mostly the same, except the syntax may be different. Check our recipe guides for ThirdParty and EmailPassword for more information. -
If you were using
ThirdPartyPasswordless
, you should now initThirdParty
andPasswordless
recipes separately. The config for the individual recipes are mostly the same, except the syntax may be different. Check our recipe guides for ThirdParty and Passwordless for more information. -
The way to get user information has changed:
-
If you are using
get_users_by_email
fromthirdpartyemailpassword
recipe:Before:
from supertokens_python.recipe.thirdpartyemailpassword.syncio import get_users_by_email user_info = get_users_by_email("public", "[email protected]")
After:
from supertokens_python.recipe.thirdparty.syncio import get_users_by_email as get_users_by_email_third_party from supertokens_python.recipe.emailpassword.syncio import get_user_by_email as get_user_by_email_emailpassword third_party_user_info = get_users_by_email_third_party("public", "[email protected]") email_password_user_info = get_user_by_email_emailpassword("public", "[email protected]") if email_password_user_info is not None: print(email_password_user_info) if len(third_party_user_info) > 0: print(third_party_user_info)
-
If you are using
get_user_id
fromthirdpartyemailpassword
recipe:Before:
from supertokens_python.recipe.thirdpartyemailpassword.syncio import get_user_by_id _ = get_user_by_id(user_id)
After:
from supertokens_python.recipe.thirdparty.syncio import ( get_user_by_id as get_user_by_id_thirdparty, ) from supertokens_python.recipe.emailpassword.syncio import ( get_user_by_id as get_user_by_id_emailpassword, ) thirdparty_user = get_user_by_id_thirdparty(user_id) if thirdparty_user is None: email_password_user = get_user_by_id_emailpassword(user_id) if email_password_user is not None: print(email_password_user) else: print(thirdparty_user)
-
If you are using
get_users_by_email
fromthirdpartypasswordless
recipe:Before:
from supertokens_python.recipe.thirdpartypasswordless.syncio import get_users_by_email user_info = get_users_by_email("public", "[email protected]")
After:
from supertokens_python.recipe.thirdparty.syncio import get_users_by_email as get_users_by_email_third_party from supertokens_python.recipe.passwordless.syncio import get_user_by_email as get_user_by_email_passwordless third_party_user_info = get_users_by_email_third_party("public", "[email protected]") passwordless_user_info = get_user_by_email_passwordless("public", "[email protected]") if passwordless_user_info is not None: print(passwordless_user_info) if len(third_party_user_info) > 0: print(third_party_user_info)
-
If you are using
get_user_id
fromthirdpartypasswordless
recipe:Before:
from supertokens_python.recipe.thirdpartypasswordless.syncio import get_user_by_id _ = get_user_by_id(user_id)
After:
from supertokens_python.recipe.thirdparty.syncio import ( get_user_by_id as get_user_by_id_thirdparty, ) from supertokens_python.recipe.passwordless.syncio import ( get_user_by_id as get_user_by_id_passwordless, ) thirdparty_user = get_user_by_id_thirdparty(user_id) if thirdparty_user is None: passwordless_user = get_user_by_id_passwordless(user_id) if passwordless_user is not None: print(passwordless_user) else: print(thirdparty_user)
-
- Improves FastAPI middleware performance using recommended ASGI middleware implementation.
- Fixes parameter mismatch in generating fake email
- Added
older_cookie_domain
config option in the session recipe. This will allow users to clear cookies from the older domain when thecookie_domain
is changed. - If
verify_session
detects multiple access tokens in the request, it will return a 401 error, prompting a refresh, even if one of the tokens is valid. refresh_post
(/auth/session/refresh
by default) API changes:- now returns 500 error if multiple access tokens are present in the request and
config.older_cookie_domain
is not set. - now clears the access token cookie if it was called without a refresh token (if an access token cookie exists and if using cookie-based sessions).
- now clears cookies from the old domain if
older_cookie_domain
is specified and multiple refresh/access token cookies exist, without updating the front-token or any of the tokens. - now a 200 response may not include new session tokens.
- now returns 500 error if multiple access tokens are present in the request and
- Fixed a bug in the
normalise_session_scope
util function that caused it to remove leading dots from the scope string.
With this update, the second argument in the session.init
function changes from cookie_secure
to older_cookie_domain
. If you're using positional arguments, you need to insert None
for older_cookie_domain
as the second argument to maintain the correct order of parameters.
Before:
from supertokens_python import init, SupertokensConfig, InputAppInfo
from supertokens_python.recipe import session
init(
supertokens_config=SupertokensConfig("..."),
app_info=InputAppInfo("..."),
framework="...",
recipe_list=[
session.init(
"example.com" # cookie_domain
True, # cookie_secure
"strict" # cookie_same_site
),
],
)
After the update:
from supertokens_python import init, SupertokensConfig, InputAppInfo
from supertokens_python.recipe import session
init(
supertokens_config=SupertokensConfig("..."),
app_info=InputAppInfo("..."),
framework="...",
recipe_list=[
session.init(
"example.com" # cookie_domain
None, # older_cookie_domain
True, # cookie_secure
"strict" # cookie_same_site
),
],
)
This update addresses an edge case where changing the cookie_domain
config on the server can lead to session integrity issues. For instance, if the API server URL is 'api.example.com' with a cookie domain of '.example.com', and the server updates the cookie domain to 'api.example.com', the client may retain cookies with both '.example.com' and 'api.example.com' domains, resulting in multiple sets of session token cookies existing.
Previously, verify_session would select one of the access tokens from the incoming request. If it chose the older cookie, it would return a 401 status code, prompting a refresh request. However, the refresh_post
API would then set new session token cookies with the updated cookie_domain
, but older cookies will persist, leading to repeated 401 errors and refresh loops.
With this update, verify_session will return a 401 error if it detects multiple access tokens in the request, prompting a refresh request. The refresh_post
API will clear cookies from the old domain if older_cookie_domain
is specified in the configuration, then return a 200 status. If older_cookie_domain
is not configured, the refresh_post
API will return a 500 error with a message instructing to set older_cookie_domain
.
Example:
apiDomain
: 'api.example.com'cookie_domain
: 'api.example.com'
Flow:
- After authentication, the frontend has cookies set with
domain=api.example.com
, but the access token has expired. - The server updates
cookie_domain
to.example.com
. - An API call requiring session with an expired access token (cookie with
domain=api.example.com
) results in a 401 response. - The frontend attempts to refresh the session, generating a new access token saved with
domain=.example.com
. - The original API call is retried, but because it sends both the old and new cookies, it again results in a 401 response.
- The frontend tries to refresh the session with multiple access tokens:
- If
older_cookie_domain
is not set, the refresh fails with a 500 error.- The user remains stuck until they clear cookies manually or
older_cookie_domain
is set.
- The user remains stuck until they clear cookies manually or
- If
older_cookie_domain
is set, the refresh clears the older cookie, returning a 200 response.- The frontend retries the original API call, sending only the new cookie (
domain=.example.com
), resulting in a successful request.
- The frontend retries the original API call, sending only the new cookie (
- If
create_new_session
now defaults to the value of thest-auth-mode
header (if available) if the configuredget_token_transfer_method
returnsany
.- Enable smooth switching between
use_dynamic_access_token_signing_key
settings by allowing refresh calls to change the signing key type of a session.
- A session is not required when calling the sign out API. Otherwise the API will return a 401 error.
- Fixes issues with the propagation of session creation/updates with django-rest-framework because the django-rest-framework wrapped the original request with it's own request object. Updates on that object were not reflecting on the original request object.
- Fixes type mismatch for FastAPI middleware.
- Relax constraints on
aiosmtplib
dependency version.
- Updates version for CICD testing
- Fixes session recipe to not pass tenant id when
revoke_across_all_tenants
orfetch_across_all_tenants
is set toTrue
- Updated fake email generation
- Fixes dashboard URI path. Now it returns the complete user given path instead of just the normalized connectionURI domain.
- Fixes
connection_uri
normalisation in the dashboard recipe. - Fixes issue with fetching of thirdparty passwordless user in dashboard: #472
- Relax constraints on
httpx
dependency version.
- Fixes an incompatibility issue with Django version 4.0 and above.
- CI/CD changes
- Fixes security issue with shared
g
objects from gunicorn: #463
- Updates LinkedIn OAuth implementation as per the latest changes.
- Fixes bug in dashboard recipe where we did not expose
USER_EMAIL_VERIFY_TOKEN_API
API.
- Adds support for configuring multiple frontend domains to be used with the same backend
- Added new
origin
property toInputAppInfo
, this can be configured to allow you to conditionally return the value of the frontend domain. This property will replacewebsite_domain
website_domain
insideInputAppInfo
is now optional. Usingorigin
recommended over usingwebsite_domain
. Usingwebsite_domain
will continue to work.
-
The order or arguments in the
InputAppInfo
has changed. If NOT using keyword arguments forapp_info
insupertokens.init
, then you will have to movewebsite_domain
like so:Before:
init( app_info=InputAppInfo( "app_name", "api_domain", "website_domain", None, # api_gateway_path None, # api_base_path None, # website_base_path ), # other configs.. )
After:
init( app_info=InputAppInfo( "app_name", "api_domain", None, # api_gateway_path None, # api_base_path None, # website_base_path "website_domain" ), # other configs.. )
-
In the session recipe, if there is an
UNAUTHORISED
orTOKEN_THEFT_DETECTED
error, the session tokens are cleared in the response regardless of if you have provided your ownerror_handlers
insession.init
- Fixes
create_reset_password_link
in the emailpassword recipe wherein we passed therid
instead of the token in the link
- Fixed spelling of
CreateResetPasswordLinkUnknownUserIdError
increate_reset_password_link
. It used to beCreateResetPasswordLinkUknownUserIdError
- Added
network_interceptor
to thesupertokens_config
ininit
.- This can be used to capture/modify all the HTTP requests sent to the core.
- Solves the issue - supertokens/supertokens-core#865
- The sync functions
create_user_id_mapping
anddelete_user_id_mapping
now take theforce
parameter as an optional argument, just like their async counterparts. - Functions
get_users_oldest_first
,get_users_newest_first
,get_user_count
,delete_user
,create_user_id_mapping
,get_user_id_mapping
,delete_user_id_mapping
andupdate_or_delete_user_id_mapping_info
now acceptuser_context
as an optional argument. - Fixed the dependencies in the example apps
- Example apps will now fetch the latest version of the frameworks
- Added
debug
flag ininit()
. If set toTrue
, debug logs will be printed.
- Fixed server error in
sign_in_up
API- There was a bug in case where the API was called with just oAuth tokens without passing the
redirect_uri_info
.
- There was a bug in case where the API was called with just oAuth tokens without passing the
- Relaxed constraint on
pyJWT
dependency.- This is done because some users face
InvalidSignatureError
when decoding the id token with the latestpyJWT
version.
- This is done because some users face
- Add
validate_access_token
function to providers- This can be used to verify the access token received from providers.
- Implemented
validate_access_token
for the Github provider.
- Add Twitter provider for thirdparty login
- Add
Cache-Control
header for jwks endpoint/jwt/jwks.json
- Add
validity_in_secs
to the return value of overridableget_jwks
recipe function.- This can be used to control the
Cache-Control
header mentioned above. - It defaults to
60
or the value set in the cache-control header returned by the core - This is optional (so you are not required to update your overrides). Returning
None
means that the header won't be set
- This can be used to control the
- Allow use of nest-asyncio when env var
SUPERTOKENS_NEST_ASYNCIO=1
. - Retry Querier request on
AsyncLibraryNotFoundError
- Handle AWS Public URLs (ending with
.amazonaws.com
) separately while extracting TLDs for SameSite attribute.
- The Dashboard recipe now accepts a new
admins
property which can be used to give Dashboard Users write privileges for the user dashboard.
- Dashboard APIs now return a status code
403
for all non-GET requests if the currently logged in Dashboard User is not listed in theadmins
array - Now ignoring protected props in the payload in
create_new_session
andcreate_new_session_without_request_response
- Handle 429 rate limiting from SaaS core instances
- Fixed bugs in thirdparty providers: Bitbucket, Boxy-SAML, and Facebook
- Fixes name of passwordless recipe function from
passwordlessSigninup
topasswordless_signinup
- Fixes apple redirect
- Fixes an issue where the user management dashboard would incorrectly show an email as unverified even if it was verified
- Added Multitenancy Recipe & always initialized by default.
- Adds Multitenancy support to all the recipes
- Added new Social login providers - LinkedIn
- Added new Multi-tenant SSO providers - Okta, Active Directory, Boxy SAML
- All APIs handled by Supertokens middleware can have an optional
tenantId
prefixed in the path. e.g.<basePath>/<tenantId>/signinup
- Following recipe functions (asyncio/syncio) have been added:
EmailPassword
create_reset_password_link
send_reset_password_email
EmailVerification
create_email_verification_link
send_email_verification_email
ThirdParty
get_provider
ThirdPartyEmailPassword
third_party_get_provider
create_reset_password_link
send_reset_password_email
ThirdPartyPasswordless
third_party_get_provider
create_reset_password_link
send_reset_password_email
- Only supporting FDI 1.17
- Core must be upgraded to 6.0
get_users_oldest_first
&get_users_newest_first
has mandatory parametertenant_id
. Pass'public'
if not using multitenancy.- Added mandatory field
tenant_id
toEmailDeliveryInterface
andSmsDeliveryInterface
. Pass'public'
if not using multitenancy. - Removed deprecated config
create_and_send_custom_email
andcreate_and_send_custom_text_message
. - EmailPassword recipe changes:
- Added mandatory
tenant_id
field toTypeEmailPasswordPasswordResetEmailDeliveryInput
- Removed
reset_password_using_token_feature
fromTypeInput
- Added
tenant_id
param tovalidate
function inTypeInputFormField
- Added mandatory
tenant_id
as first parameter to the following recipe index functions:sign_up
sign_in
get_user_by_email
create_reset_password_token
reset_password_using_token
- Added mandatory
tenantId
in the input for the following recipe interface functions. If any of these functions are overridden, they need to be updated accordingly:sign_up
sign_in
get_user_by_email
create_reset_password_token
reset_password_using_token
update_email_or_password
- Added mandatory
tenantId
in the input for the following API interface functions. If any of these functions are overridden, they need to be updated accordingly:email_exists_get
generate_password_reset_token_post
password_reset_post
sign_in_post
sign_up_post
- Added mandatory
- EmailVerification recipe changes:
- Added mandatory
tenant_id
field toTypeEmailVerificationEmailDeliveryInput
- Added mandatory
tenant_id
as first parameter to the following recipe index functions:create_email_verification_token
verify_email_using_token
revoke_email_verification_tokens
- Added mandatory
tenantId
in the input for the following recipe interface functions. If any of these functions are overridden, they need to be updated accordingly:create_email_verification_token
verify_email_using_token
revoke_email_verification_tokens
- Added mandatory
tenantId
in the input for the following API interface functions. If any of these functions are overridden, they need to be updated accordingly:verify_email_post
- Added mandatory
- Passwordless recipe changes:
- Added
tenant_id
param tovalidate_email_address
,validate_phone_number
andget_custom_user_input_code
functions inTypeInput
- Added mandatory
tenant_id
field toTypePasswordlessEmailDeliveryInput
andTypePasswordlessSmsDeliveryInput
- Added mandatory
tenant_id
in the input to the following recipe index functions:create_code
create_new_code_for_device
get_user_by_email
get_user_by_phone_number
update_user
revoke_code
list_codes_by_email
list_codes_by_phone_number
list_codes_by_device_id
list_codes_by_pre_auth_session_id
sign_in_up
- Added mandatory
tenant_id
in the input for the following recipe interface functions. If any of these functions are overridden, they need to be updated accordingly:create_code
create_new_code_for_device
consume_code
get_user_by_email
get_user_by_phone_number
revoke_all_codes
revoke_code
list_codes_by_email
list_codes_by_phone_number
list_codes_by_device_id
list_codes_by_pre_auth_session_id
- Added mandatory
tenant_id
in the input for the following API interface functions. If any of these functions are overridden, they need to be updated accordingly:create_code_post
resend_code_post
consume_code_post
email_exists_get
phone_number_exists_get
- Added
- ThirdParty recipe changes
- The providers array in
sign_in_up_feature
acceptsList[ProviderInput]
instead ofList[Provider]
.Provider
interface is re-written. Refer migration section for more info. - Removed
sign_in_up
and addedmanually_create_or_update_user
instead in the recipe index functions. - Added
manually_create_or_update_user
to recipe interface which is being called by the function mentioned above.manually_create_or_update_user
recipe interface function should not be overridden as it is not going to be called by the SDK in the sign in/up flow.sign_in_up
recipe interface functions is not removed and is being used by the sign in/up flow.
- Added mandatory
tenant_id
as first parameter to the following recipe index functions:get_users_by_email
get_user_by_third_party_info
- Added mandatory
tenant_id
in the input for the following recipe interface functions. If any of these functions are overridden, they need to be updated accordingly:get_users_by_email
get_user_by_third_party_info
sign_in_up
- Added mandatory
tenant_id
in the input for the following API interface functions. If any of these functions are overridden, they need to be updated accordingly:authorisation_url_get
sign_in_up_post
- Updated
sign_in_up
recipe interface function in thirdparty with new parameters:o_auth_tokens
- contains all the tokens (access_token, id_token, etc.) as returned by the providerraw_user_info_from_provider
- contains all the user profile info as returned by the provider
- Updated
authorisation_url_get
API- Changed: Doesn't accept
client_id
anymore and acceptsclient_type
instead to determine the matching config - Added: optional
pkce_code_verifier
in the response, to support PKCE
- Changed: Doesn't accept
- Updated
sign_in_up_post
API- Removed:
client_id
,redirect_uri
,auth_code_response
andcode
from the input - Instead,
- accepts
client_type
to determine the matching config - One of redirectURIInfo (for code flow) or oAuthTokens (for token flow) is required
- accepts
- Removed:
- Updated
apple_redirect_handler_post
- to accept all the form fields instead of just the code
- to use redirect URI encoded in the
state
parameter instead of using the websiteDomain config. - to use HTTP 303 instead of javascript based redirection.
- The providers array in
- Session recipe changes
- Added mandatory
tenant_id
as first parameter to the following recipe index functions:create_new_session
create_new_session_without_request_response
validate_claims_in_jwt_payload
- Added mandatory
tenant_id
in the input for the following recipe interface functions. If any of these functions are overridden, they need to be updated accordingly:create_new_session
get_global_claim_validators
- Added
tenant_id
andrevoke_across_all_tenants
params torevoke_all_sessions_for_user
in the recipe interface. - Added
tenant_id
andfetch_across_all_tenants
params toget_all_session_handles_for_user
in the recipe interface. - Added
get_tenant_id
function toSessionContainerInterface
- Added
tenant_id
tofetch_value
function inPrimitiveClaim
,PrimitiveArrayClaim
.
- Added mandatory
- UserRoles recipe changes
- Added mandatory
tenant_id
as first parameter to the following recipe index functions:add_role_to_user
remove_user_role
get_roles_for_user
get_users_that_have_role
- Added mandatory
tenant_id
in the input for the following recipe interface functions. If any of these functions are overridden, they need to be updated accordingly:add_role_to_user
remove_user_role
get_roles_for_user
get_roles_for_user
- Added mandatory
- Similar changes in combination recipes (thirdpartyemailpassword and thirdpartypasswordless) have been made
- Even if thirdpartyemailpassword and thirdpartpasswordless recipes do not have a providers array as an input, they will still expose the third party recipe routes to the frontend.
- Returns 400 status code in emailpassword APIs if the input email or password are not of type string.
- Recipe function changes:
- Added optional
tenant_id_for_password_policy
param toEmailPassword.update_email_or_password
,ThirdPartyEmailPassword.update_email_or_password
- Added optional param
tenant_id
toSession.revoke_all_sessions_for_user
. If tenantId is undefined, sessions are revoked across all tenants - Added optional param
tenant_id
toSession.get_all_session_handles_for_user
. If tenantId is undefined, sessions handles across all tenants are returned
- Added optional
- Adds optional param
tenant_id
toget_user_count
which returns total count across all tenants if not passed. - Adds protected prop
tId
to the accessToken payload - Adds
includes_any
claim validator toPrimitiveArrayClaim
- Fixed an issue where certain Dashboard API routes would return a 404 for Hapi
-
To call any recipe function that has
tenant_id
added to it, pass'public
'Before:
emailpassword.asyncio.sign_up("[email protected]", "password")
After:
emailpassword.asyncio.sign_up("public", "[email protected]", "password")
-
Input for provider array change as follows:
Before:
google_provider = thirdparty.Google( client_id="...", client_secret="...", )
After:
google_provider = thirdparty.ProviderConfig( third_party_id="google", clients=[thirdparty.ProviderClientConfig(client_id="...", client_secret="...")], )
-
Single instance with multiple clients of each provider instead of multiple instances of them. Also use
client_type
to differentiate them.client_type
passed from the frontend will be used to determine the right config.is_default
option has been removed andclient_type
is expected to be passed when there are more than one client. If there is only one client,client_type
is optional and will be used by default.Before:
providers = [ thirdparty.Google( is_default=True, client_id="clientid1", client_secret="...", ), thirdParty.Google( client_id="clientid2", client_secret="...", ), ]
After:
providers = [ thirdparty.ProviderConfig( third_party_id="google", clients=[ thirdparty.ProviderClientConfig(client_type="web", client_id= "clientid1", client_secret= "..."), thirdparty.ProviderClientConfig(client_type="mobile", client_id="clientid2", client_secret="..."), ], ) ]
-
Change in the implementation of custom providers
- All config is part of
ProviderInput
- To provide implementation for
get_profile_info
- either use
user_info_endpoint
,user_info_endpoint_query_params
anduser_info_map
to fetch the user info from the provider - or specify custom implementation in an override for
get_user_info
(override example in the next section)
- either use
Before:
class CustomProvider(Provider): def get_access_token_api_info( self, redirect_uri: str, auth_code_from_request: str, user_context: Dict[str, Any], ) -> AccessTokenAPI: params = { "client_id": self.client_id, "client_secret": self.client_secret, "grant_type": "...", "code": auth_code_from_request, "redirect_uri": redirect_uri, } return AccessTokenAPI(self.access_token_api_url, params) def get_authorisation_redirect_api_info( self, user_context: Dict[str, Any] ) -> AuthorisationRedirectAPI: params: Dict[str, Any] = { "scope": "...", "response_type": "...", "client_id": self.client_id, } return AuthorisationRedirectAPI(self.authorisation_redirect_url, params) def get_redirect_uri(self, user_context: Dict[str, Any]) -> Union[None, str]: return None def get_client_id(self, user_context: Dict[str, Any]) -> str: return self.client_id async def get_profile_info( self, auth_code_response: Dict[str, Any], user_context: Dict[str, Any] ) -> UserInfo: return UserInfo(id="...", UserInfoEmail(email="...", True))
After:
custom_provider = thirdparty.Provider( config=thirdparty.ProviderConfig( third_party_id="custom", clients=[ thirdparty.ProviderConfigClient( client_id="...", client_secret="...", ), ], authorization_endpoint="...", authorization_endpoint_query_params={}, token_endpoint="...", token_endpoint_body_params={}, user_info_endpoint="...", user_info_endpoint_query_params={}, user_info_map=UserInfoMap( from_user_info_api=UserFields( user_id="id", email="email", email_verified="email_verified", ), ), ), )
Also, if the custom provider supports openid, it can automatically discover the endpoints
custom_provider = thirdparty.ProviderInput( config=thirdparty.ProviderConfig( third_party_id="custom", clients=[ thirdparty.ProviderConfigClient( client_id="...", client_secret="...", ), ], oidc_discovery_endpoint="...", user_info_map=UserInfoMap( from_user_info_api=UserFields( user_id="id", email="email", email_verified="email_verified", ), ), ), )
Note: The SDK will fetch the oauth2 endpoints from the provider's OIDC discovery endpoint. No need to
/.well-known/openid-configuration
to theoidcDiscoveryEndpoint
config. For eg. ifoidcDiscoveryEndpoint
is set to"https://accounts.google.com/"
, the SDK will fetch the endpoints from"https://accounts.google.com/.well-known/openid-configuration"
- All config is part of
-
Any of the functions in the TypeProvider can be overridden for custom implementation
- Overrides can do the following:
- update params, headers dynamically for the authorization redirect url or in the exchange of code to tokens
- add custom logic to exchange code to tokens
- add custom logic to get the user info
def override(oi): oi_get_authorisation_redirect_url = oi.get_authorisation_redirect_url oi_exchange_auth_code_for_oauth_tokens = oi.exchange_auth_code_for_oauth_tokens oi_get_user_info = oi.get_user_info async def get_authorisation_redirect_url( # pylint: disable=no-self-use redirect_uri_on_provider_dashboard: str, user_context: Dict[str, Any], ) -> AuthorisationRedirect: res = await oi_get_authorisation_redirect_url(redirect_uri_on_provider_dashboard, user_context) # ... return res async def exchange_auth_code_for_oauth_tokens( # pylint: disable=no-self-use redirect_uri_info: RedirectUriInfo, user_context: Dict[str, Any], ) -> Dict[str, Any]: res = await oi_exchange_auth_code_for_oauth_tokens(redirect_uri_info, auth_code, user_context) # ... return res async def get_user_info( # pylint: disable=no-self-use oauth_tokens: Dict[str, Any], user_context: Dict[str, Any], ) -> UserInfo: res = await oi_get_user_info(oauth_tokens, user_context) # ... return res oi.get_authorisation_redirect_url = get_authorisation_redirect_url oi.exchange_auth_code_for_oauth_tokens = exchange_auth_code_for_oauth_tokens oi.get_user_info = get_user_info return oi custom_provider = thirdparty.ProviderInput( config=thirdparty.ProviderConfig( third_party_id="custom", clients=[ thirdparty.ProviderConfigClient( client_id="...", client_secret="...", ), ], oidc_discovery_endpoint="...", user_info_map=UserInfoMap( from_user_info_api=UserFields( user_id="id", email="email", email_verified="email_verified", ), ), ), override=override )
- Overrides can do the following:
-
To get access token and raw user info from the provider, override the signInUp function
def override_functions(oi): oi_sign_in_up = oi.sign_in_up async def sign_in_up( third_party_id: str, third_party_user_id: str, email: str, oauth_tokens: Dict[str, Any], raw_user_info_from_provider: RawUserInfoFromProvider, tenant_id: str, user_context: Dict[str, Any], ) -> SignInUpOkResult: res = await oi_sign_in_up(third_party_id, third_party_user_id, email, oauth_tokens, raw_user_info_from_provider, tenant_id, user_context) # res.oauth_tokens['access_token'] # res.oauth_tokens['id_token'] # res.raw_user_info_from_provider.from_user_info_api # res.raw_user_info_from_provider.from_id_token_payload return res thirdparty.init( override=thirdparty.InputOverrideConfig(functions=override_functions) )
-
Request body of thirdparty signinup API has changed
-
If using auth code:
Before:
{ "thirdPartyId": "...", "clientId": "...", "redirectURI": "...", // optional "code": "..." }
After:
{ "thirdPartyId": "...", "clientType": "...", "redirectURIInfo": { "redirectURIOnProviderDashboard": "...", // required "redirectURIQueryParams": { "code": "...", "state": "..." // ... all callback query params }, "pkceCodeVerifier": "..." // optional, use this if using PKCE flow } }
-
If using tokens:
Before:
{ "thirdPartyId": "...", "clientId": "...", "redirectURI": "...", "authCodeResponse": { "access_token": "...", // required "id_token": "..." } }
After:
{ "thirdPartyId": "...", "clientType": "...", "oAuthTokens": { "access_token": "...", // now optional "id_token": "..." // rest of the oAuthTokens as returned by the provider } }
-
- Compatible with Core>=6.0.0 (CDI 4.0)
- Compatible with frontend SDKs:
- Anti csrf check should happen only when access token is passed while session is optional
verify_session
middleware now handles supertokens related errors as well.
- Fixes error message in querier.
- Relax constraints on
httpx
dependency version
- Remove constraints on
cryptograpy
dependency version and letpyjwt
library handle it
- Use
useStaticSigningKey
instead ofuse_static_signing_key
increate_jwt
function. This was a bug in the code. - Use request library instead of urllib to fetch JWKS keys (#344)
- Throw error when
verify_sesion
is used with a view that allowsOPTIONS
orTRACE
requests - Allow
verify_session
decorator to be with@app.before_request
in Flask without returning a response
- Update email templates to fix an issue with styling on some email clients
- Adds additional debug logs whenever the SDK throws a
TRY_REFRESH_TOKEN
orUNAUTHORISED
error to make debugging easier
- Added a new
get_request_from_user_context
function that can be used to read the original network request from the user context in overridden APIs and recipe functions
- Adds missing
check_database
boolean inverify_session
- Made the access token string optional in the overrideable
get_session
function - Moved checking if the access token is defined into the overrideable
get_session
function
- Added support for CDI version
2.21
- Dropped support for CDI version
2.8
-2.20
- Changed the interface and configuration of the Session recipe, see below for details. If you do not use the Session recipe directly and do not provide custom configuration, then no migration is necessary.
get_access_token_payload
will now return standard (sub
,iat
,exp
) claims and some SuperTokens specific claims along the user defined ones inget_access_token_payload
.- Some claim names are now prohibited in the root level of the access token payload:
- They are:
sub
,iat
,exp
,sessionHandle
,parentRefreshTokenHash1
,refreshTokenHash1
,antiCsrfToken
- If you used these in the root level of the access token payload, then you'll need to migrate your sessions or they will be logged out during the next refresh
- These props should be renamed (e.g., by adding a prefix) or moved inside an object in the access token payload
- You can migrate these sessions by updating their payload to match your new structure, by calling
merge_into_access_token_payload
- They are:
- New access tokens are valid JWTs now
- They can be used directly (i.e.: by calling
get_access_token
on the session) if you need a JWT - The
jwt
prop in the access token payload is removed
- They can be used directly (i.e.: by calling
- Changed the Session recipe interface -
create_new_session
,get_session
andrefresh_session
overrides now do not take response and request and return status instead of throwing
- Added
use_dynamic_access_token_signing_key
(defaults toTrue
) option to the Session recipe config - Added
expose_access_token_to_frontend_in_cookie_based_auth
(defaults toFalse
) option to the Session recipe config - JWT and OpenId related configuration has been removed from the Session recipe config. If necessary, they can be added by initializing the OpenId recipe before the Session recipe.
- Renamed
get_session_data
toget_session_data_from_database
to clarity that it always hits the DB - Renamed
update_session_data
toupdate_session_data_in_database
- Renamed
session_data
tosession_data_in_database
inSessionInformation
and the input tocreate_new_session
- Added new
check_database
param toverify_session
andget_session
- Removed
status
fromjwks_get
output (function & API) - Added new optional
use_static_signing_key
param tocreateJWT
- Removed deprecated
update_access_token_payload
andregenerate_access_token
from the Session recipe interface - Removed
get_access_token_lifetime_ms
andget_refresh_token_lifetime_ms
functions
- The Session recipe now always initializes the OpenID recipe if it hasn't been initialized.
- Refactored how access token validation is done
- Removed the handshake call to improve start-up times
- Added support for new access token version
- added optional password policy check in
update_email_or_password
- Added
create_new_session_without_request_response
,get_session_without_request_response
,refresh_session_without_request_response
to the Session recipe. - Added
get_all_session_tokens_dangerously
to session objects (SessionContainer
) - Added
attach_to_request_response
to session objects (SessionContainer
)
- You need to update the core version
- There are manual migration steps needed. Check out the core changelogs for more details.
- Add
expose_access_token_to_frontend_in_cookie_based_auth=true
to the Session recipe config on the backend if you need to access the JWT on the frontend. - Choose a prop from the following list. We'll use
sub
in the code below, but you can replace it with another from the list if you used it in a custom access token payload.sub
iat
exp
sessionHandle
- On the frontend where you accessed the JWT before by:
(await Session.getAccessTokenPayloadSecurely()).jwt
update to:
let jwt = null;
const accessTokenPayload = await Session.getAccessTokenPayloadSecurely();
if (accessTokenPayload.sub !== undefined) {
jwt = await Session.getAccessToken();
} else {
// This branch is only required if there are valid access tokens created before the update
// It can be removed after the validity period ends
jwt = accessTokenPayload.jwt;
}
- On the backend if you accessed the JWT before by
session.get_access_token_payload()['jwt']
please update to:
from supertokens_python.recipe.session.interfaces import SessionContainer
session: SessionContainer = ...
access_token_payload = await session.get_access_token_payload()
if access_token_payload.get('sub') is not None:
jwt = await session.get_access_token()
else:
# This branch is only required if there are valid access tokens created before the update
# It can be removed after the validity period ends
jwt = access_token_payload['jwt']
- You can add an issuer claim to access tokens by overriding the
create_new_session
function in the session recipe init.- Check out https://supertokens.com/docs/passwordless/common-customizations/sessions/claims/access-token-payload#during-session-creation for more information
- You can add an issuer claim to JWTs created by the JWT recipe by passing the
iss
claim as part of the payload. - You can set the OpenId discovery configuration as follows:
Before:
from supertokens_python import init
from supertokens_python.recipe import session
init(
app_info="...",
recipe_list=[
session.init(jwt=session.JWTConfig(enable=True, issuer="..."))
]
)
After:
from typing import Dict, Any
from supertokens_python import init
from supertokens_python.recipe import session, openid
from supertokens_python.recipe.openid.interfaces import RecipeInterface as OpenIDRecipeInterface
from supertokens_python.recipe.openid.interfaces import GetOpenIdDiscoveryConfigurationResult
async def openid_functions_override(oi: OpenIDRecipeInterface):
async def get_openid_discovery_configuration(_: Dict[str, Any]):
return GetOpenIdDiscoveryConfigurationResult(
issuer="your issuer",
jwks_uri="https://your.api.domain/auth/jwt/keys"
)
oi.get_open_id_discovery_configuration = get_openid_discovery_configuration
return oi
init(
app_info="...",
recipe_list=[
session.init(
get_token_transfer_method= lambda *_: "header",
override=session.InputOverrideConfig(
openid_feature=openid.InputOverrideConfig(
functions=openid_functions_override
)
)
)
]
)
Related functions/prop names have changes (session_data
became session_data_from_database
):
- Renamed
get_session_data
toget_session_data_from_database
to clarify that it always hits the DB - Renamed
update_session_data
toupdate_session_data_in_database
- Renamed
session_data
tosession_data_in_database
inSessionInformationResult
and the input tocreate_new_session
- You should now set
check_database
to true in theverify_session
params.
- You should now set
use_dynamic_access_token_signing_key
in the Session recipe config.
- Update you application logic to rename those props (e.g., by adding a prefix)
- Update the session recipe config (in this example
sub
is the protected property we are updating by adding theapp
prefix):
Before:
from typing import Any, Dict, Optional
from supertokens_python.recipe.session.interfaces import RecipeInterface as SessionRecipeInterface
from supertokens_python.recipe import session
async def override_session_functions(oi: SessionRecipeInterface):
oi_create_new_session = oi.create_new_session
async def create_new_session(
user_id: str,
access_token_payload: Optional[Dict[str, Any]],
session_data_in_database: Optional[Dict[str, Any]],
disable_anti_csrf: Optional[bool],
user_context: Dict[str, Any],
):
return oi_create_new_session(
user_id,
{**access_token_payload, "sub": access_token_payload["userId"] + "!!!" }
session_data_in_database,
disable_anti_csrf,
user_context,
)
oi.create_new_session = create_new_session
session.init(
override=session.InputOverrideConfig(functions=override_session_functions)
)
After:
from typing import Any, Dict, Optional
from supertokens_python.recipe.session.interfaces import RecipeInterface as SessionRecipeInterface
from supertokens_python.recipe import session
async def override_session_functions(oi: SessionRecipeInterface):
oi_get_session = oi.get_session
oi_create_new_session = oi.create_new_session
async def get_session(
access_token: str,
anti_csrf_token: Optional[str],
anti_csrf_check: Optional[bool] = None,
check_database: Optional[bool] = None,
override_global_claim_validators: Optional[
Callable[
[List[SessionClaimValidator], SessionContainer, Dict[str, Any]],
MaybeAwaitable[List[SessionClaimValidator]],
]
] = None,
user_context: Optional[Dict[str, Any]] = None,
):
result = oi_get_session(input)
if result:
orig_payload = result.get_access_token_payload()
if orig_payload["appSub"] is None:
await result.merge_into_access_token_payload({"appSub": orig_payload["sub"], "sub": None})
return result
async def create_new_session(
user_id: str,
access_token_payload: Optional[Dict[str, Any]],
session_data_in_database: Optional[Dict[str, Any]],
disable_anti_csrf: Optional[bool],
user_context: Dict[str, Any],
):
return oi_create_new_session(
user_id,
{**access_token_payload, "appSub": access_token_payload["userId"] + "!!!" }
session_data_in_database,
disable_anti_csrf,
user_context
)
oi.get_session = get_session
oi.create_new_session = create_new_session
session.init(
override=session.InputOverrideConfig(
functions=override_session_functions,
)
)
This example uses get_session
, but the changes required for the other ones are very similar. Before:
from typing import Any, Dict, Optional
from supertokens_python.recipe.session.interfaces import RecipeInterface as SessionRecipeInterface
from supertokens_python.recipe import session
async def override_session_functions():
oi.get_session = oi.get_session
async def get_session(
request: Any,
access_token: str,
anti_csrf_token: Optional[str],
anti_csrf_check: Optional[bool] = None,
check_database: Optional[bool] = None,
override_global_claim_validators: Optional[
Callable[
[List[SessionClaimValidator], SessionContainer, Dict[str, Any]],
MaybeAwaitable[List[SessionClaimValidator]],
]
] = None,
user_context: Optional[Dict[str, Any]] = None,
):
print(request)
try:
_session = await oi_get_session(
request,
access_token,
anti_csrf_token,
anti_csrf_check,
check_database,
override_global_claim_validators,
user_context,
)
print(_session)
return _session
except Exception as e:
print(e)
raise e
session.init(
override=session.InputOverrideConfig(
functions=override_session_functions,
)
)
After:
from typing import Any, Dict, Optional
from supertokens_python.recipe.session.interfaces import RecipeInterface as SessionRecipeInterface
from supertokens_python.recipe import session
async def override_session_functions():
oi.get_session = oi.get_session
async def get_session(
access_token: str,
anti_csrf_token: Optional[str],
anti_csrf_check: Optional[bool] = None,
check_database: Optional[bool] = None,
override_global_claim_validators: Optional[
Callable[
[List[SessionClaimValidator], SessionContainer, Dict[str, Any]],
MaybeAwaitable[List[SessionClaimValidator]],
]
] = None,
user_context: Optional[Dict[str, Any]] = None,
):
request = user_context["_default"]["request"]
print(request)
session_res = await oi_get_session(
request,
access_token,
anti_csrf_token,
anti_csrf_check,
check_database,
override_global_claim_validators,
user_context,
)
if session_res.status == "OK":
print(session_res.session)
else:
print(session_res.status)
print(session_res.error)
return session_res
session.init(
override=session.InputOverrideConfig(
functions=override_session_functions,
)
)
- Added missing arguments in
get_users_newest_first
andget_users_oldest_first
- Fixed an issues that threw 500 when changing password for user from dashboard
- Email template for verify email updated
- Adds search APIs to the dashboard recipe
- Adds a telemetry API to the dashboard recipe
- Update all example apps to initialise dashboard recipe
- Login with gitlab (single tenant only) and bitbucket
- Adds APIs and logic to the dashboard recipe to enable email password based login
- Fix expiry time of access token cookie.
- Email template updates
- The frontend SDK should be updated to a version supporting the header-based sessions!
- supertokens-auth-react: >= 0.31.0
- supertokens-web-js: >= 0.5.0
- supertokens-website: >= 16.0.0
- supertokens-react-native: >= 4.0.0
- supertokens-ios >= 0.2.0
- supertokens-android >= 0.3.0
- supertokens-flutter >= 0.1.0
- Only supporting FDI 1.16
- Added support for authorizing requests using the
Authorization
header instead of cookies- Added
get_token_transfer_method
config option - Check out https://supertokens.com/docs/thirdpartyemailpassword/common-customizations/sessions/token-transfer-method for more information
- Added
- Remove constraints on
werkzeug
version
- Add missing
original
attribute to flask response and remove logic for cases whereresponse
isNone
- Relax PyJWT version constraints #272
- Fix django cookie expiry time format to make it consistent with other frameworks: #267
- Updates dashboard version
- Updates user GET API for the dashboard recipe
- Fixes issue of sign up API not sending a
FIELD_ERROR
response in case of duplicate email: #264
- Fixes issue where if send_email is overridden with a different email, it will reset that email.
- APIs for user details to the dashboard recipe
- Updates dashboard version to 0.2
- Add tests for different scenarios while revoking session during session refresh call
- Remove
jsonschema
from package requirements
- Update session claims in email verification token generation API in case the session claims are outdated.
- Fix cookie_same_site for subdomains #239
- Add
to_json
method toClaimValidationError
class.
- Relaxes typing_extensions constraint
- Update frontend integration test servers for /angular and /testError tests
- Updated google token endpoint.
- Removed default
default_max_age
from session claim base classes - Added a 5 minute
default_max_age
to UserRoleClaim, PermissionClaim and EmailVerificationClaim - Fix Repetition of root_path in supertokens mididdlware for fastapi #230
- Email verification endpoints will now clear the session if called by a deleted/unknown user
- Adds dashboard recipe
- Added a
username
field to theSMTPSettings
model for passing custom SMTP server username.
- Made the
email
parameter optional inunverify_email
,revoke_email_verification_tokens
,is_email_verified
,verify_email_using_token
,create_email_verification_token
of theEmailVerification
recipe.
- Support for FDI 1.15
- Added support for session claims with related interfaces and classes.
- Added
on_invalid_claim
optional error handler to send InvalidClaim error responses. - Added
INVALID_CLAIMS
(InvalidClaimError
) toSessionErrors
. - Added
invalid_claim_status_code
optional config to set the status code of InvalidClaim errors. - Added
override_global_claim_validators
as param ofget_session
andverify_session
. - Added
merge_into_access_token_payload
to the Session recipe and session objects which should be preferred to the now deprecatedupdate_access_token_payload
. - Added
EmailVerificationClaim
,UserRoleClaim
andPermissionClaim
. These claims are now added to the access token payload by default by their respective recipes. - Added
assert_claims
,validate_claims_for_session_handle
,validate_claims_in_jwt_payload
to the Session recipe to support validation of the newly added claims. - Added
fetch_and_set_claim
,get_claim_value
,set_claim_value
andremove_claim
to the Session recipe to manage claims. - Added
assert_claims
,fetch_and_set_claim
,get_claim_value
,set_claim_value
andremove_claim
to session objects to manage claims. - Added session to the input of
generate_email_verify_token_post
,verify_email_post
,is_email_verified_get
. - Adds default userContext for verifySession calls that contains the request object.
- Removes support for FDI <= 1.14
- Changed
sign_in_up
third party recipe function to accept just the email asstr
(removedemail_verified: bool
). - The frontend SDK should be updated to a version supporting session claims!
- supertokens-auth-react: >= 0.25.0
- supertokens-web-js: >= 0.2.0
EmailVerification
recipe is now not initialized as part of auth recipes, it should be added to therecipe_list
directly instead usingemailverification.init()
.- Email verification related overrides (
email_verification_feature
attr ofoverride
) moved from auth recipes into theEmailVerification
recipe config. - Email verification related configs (
email_verification_feature
attr) moved from auth recipes into theEmailVerification
config object root. - ThirdParty recipe no longer takes
email_delivery
config. useemailverification
recipe'semail_delivery
instead. - Moved email verification related configs from the
email_delivery
config of auth recipes into a separateEmailVerification
email delivery config. - Updated return type of
get_email_for_user_id
in theEmailVerification
recipe config. It should now return an object with status. - Removed
get_reset_password_url
,get_email_verification_url
,get_link_domain_and_path
. Changing these urls can be done in the email delivery configs instead. - Removed
unverify_email
,revoke_email_verification_tokens
,is_email_verified
,verify_email_using_token
andcreate_email_verification_token
from auth recipes. These should be called on theEmailVerification
recipe instead. - Changed function signature for email verification APIs to accept a session as an input.
- Changed Session API interface functions:
refresh_post
now returns a Session container object.sign_out_post
now takes in an optional session object as a parameter.
Before:
from supertokens_python import init, SupertokensConfig, InputAppInfo
from supertokens_python.recipe import emailpassword
from supertokens_python.recipe.emailverification.utils import OverrideConfig
init(
supertokens_config=SupertokensConfig("..."),
app_info=InputAppInfo("..."),
framework="...",
recipe_list=[
emailpassword.init(
# these options should be moved into the EmailVerification config:
email_verification_feature=emailpassword.InputEmailVerificationConfig("..."),
override=emailpassword.InputOverrideConfig(
email_verification_feature=OverrideConfig(
# these overrides should be moved into the EmailVerification overrides
"..."
)
),
),
],
)
After the update:
from supertokens_python import init, SupertokensConfig, InputAppInfo
from supertokens_python.recipe import emailpassword, emailverification
init(
supertokens_config=SupertokensConfig("..."),
app_info=InputAppInfo("..."),
framework="...",
recipe_list=[
emailverification.init(
"...", # EmailVerification config
override=emailverification.OverrideConfig(
# overrides
"..."
),
),
emailpassword.init(),
],
)
If you turn on email verification your email-based passwordless users may be redirected to an email verification screen in their existing session. Logging out and logging in again will solve this problem or they could click the link in the email to verify themselves.
You can avoid this by running a script that will:
- list all users of passwordless
- create an emailverification token for each of them if they have email addresses
- user the token to verify their address
Something similar to this script:
from supertokens_python import init, SupertokensConfig, InputAppInfo
from supertokens_python.recipe import passwordless, emailverification, session
from supertokens_python.recipe.passwordless import ContactEmailOrPhoneConfig
from supertokens_python.syncio import get_users_newest_first
from supertokens_python.recipe.emailverification.syncio import create_email_verification_token, verify_email_using_token
from supertokens_python.recipe.emailverification.interfaces import CreateEmailVerificationTokenOkResult
init(
supertokens_config=SupertokensConfig("http://localhost:3567"),
app_info=InputAppInfo(
app_name="SuperTokens Demo",
api_domain="https://api.supertokens.io",
website_domain="supertokens.io",
),
framework="fastapi",
recipe_list=[
emailverification.init("REQUIRED"),
passwordless.init(
contact_config=ContactEmailOrPhoneConfig(),
flow_type="USER_INPUT_CODE_AND_MAGIC_LINK",
),
session.init(),
],
)
def verify_email_for_passwordless_users():
pagination_token = None
done = False
while not done:
res = get_users_newest_first(
limit=100,
pagination_token=pagination_token,
include_recipe_ids=["passwordless"]
)
for user in res.users:
if user.email is not None:
token_res = create_email_verification_token(user.user_id, user.email)
if isinstance(token_res, CreateEmailVerificationTokenOkResult):
verify_email_using_token(token_res.token)
done = res.next_pagination_token is None
if not done:
pagination_token = res.next_pagination_token
verify_email_for_passwordless_users()
The UserRoles
recipe now adds role and permission information into the access token payload by default. If you are already doing this manually, this will result in duplicate data in the access token.
- You can disable this behaviour by setting
skip_adding_roles_to_access_token
andskip_adding_permissions_to_access_token
to true in the recipe init. - Check how to use the new claims in the updated guide: https://supertokens.com/docs/userroles/protecting-routes
- Add support for User ID Mapping using
create_user_id_mapping
,get_user_id_mapping
,delete_user_id_mapping
,update_or_delete_user_id_mapping
functions
- Send FORM_FIELD error with 200 status code instead of 500 on invalid request body or when user passes non-string values as email ID for
/auth/signin
- Add to test to ensure that overrides are applying correctly in methods called on SessionContainer instances
- Make
user_context
optional in userroles recipe syncio functions.
- Added
pdoc
template files to project insidedocs-templates
directory - Updated
build-docs
in Makefile to usedocs-templates
as the template directory while generating docs usingpdoc
- Updated
html.mako
template to have a singleh1
tag and have a default meta description tag
- Relax version requirements for
httpx
,cryptography
, andasgiref
to fix #207
- Update tests to cover
resend_code
feature inpasswordless
andthirdpartypasswordless
recipe. - Update usermetadata tests to ensure that utf8 chars are supported.
- Mark tests as skipped if core version requirements are not met.
- Use black instead of
autopep8
to format code. - Add frontend integration tests for
django2x
- Clears cookies when
revoke_session
is called using the session container, even if the session did not exist from before: supertokens/supertokens-node#343
- Change request arg type in session recipe functions from Any to BaseRequest.
- Changes session function recipe interfaces to not throw an
UNAUTHORISED
error when the input is a session_handle: https://github.com/supertokens/backend/issues/83get_session_information
now returnsNone
if the session does not exist.update_session_data
now returnsFalse
if the inputsession_handle
does not exist.update_access_token_payload
now returnsFalse
if the inputsession_handle
does not exist.regenerate_access_token
now returnsNone
if the input access token'ssession_handle
does not exist.- The
session_class
functions have not changed in behaviour and still throwUNAUTHORISED
error. This works cause thesession_class
works on the current session and not some other session.
- Adds default
user_context
for API calls that contains the request object. It can be used in APIs / functions override like this:
def apis_override_email_password(param: APIInterface):
og_sign_in_post = param.sign_in_post
async def sign_in_post(
form_fields: List[FormField],
api_options: APIOptions,
user_context: Dict[str, Any],
):
req = user_context.get("_default", {}).get("request")
if req:
# do something with the request
return await og_sign_in_post(form_fields, api_options, user_context)
param.sign_in_post = sign_in_post
return param
def functions_override_email_password(param: RecipeInterface):
og_sign_in = param.sign_in
async def sign_in(email: str, password: str, user_context: Dict[str, Any]):
req = user_context.get("_default", {}).get("request")
if req:
# do something with the request
return await og_sign_in(email, password, user_context)
param.sign_in = sign_in
return param
init(
...,
recipe_list=[
emailpassword.init(
override=emailpassword.InputOverrideConfig(
apis=apis_override_email_password,
functions=functions_override_email_password,
)
),
session.init(),
],
)
- Add more details in the
CONTRIBUTING.md
to make it beginner friendly.
- Introduce
userroles
recipe.
from supertokens_python import InputAppInfo, SupertokensConfig, init
from supertokens_python.recipe import userroles
from supertokens_python.recipe.userroles.asyncio import create_new_role_or_add_permissions, add_role_to_user
init(
supertokens_config=SupertokensConfig('http://localhost:3567'),
app_info=InputAppInfo(
app_name='SuperTokens Demo',
api_domain='https://api.supertokens.io',
website_domain='supertokens.io'
),
framework='flask',
recipe_list=[userroles.init()]
)
user_id = "userId"
role = "role"
permissions = ["perm1", "perm2"]
# Functions to use inside your views:
# Create a new role with a few permissions:
result = await create_new_role_or_add_permissions(role, permissions)
# Add role to the user:
result = await add_role_to_user(user_id, role)
# Check documentation for more examples..
- Fixes Cookie same_site config validation.
- Remove
<Recipe>(Email|SMS)TemplateVars
in favour of(Email|SMS)TemplateVars
for better DX.
- supertokens/supertokens-node#220
- Adds
{status: "GENERAL_ERROR", message: string}
as a possible output to all the APIs. - Changes
FIELD_ERROR
output status in third party recipe API to beGENERAL_ERROR
. - Replaced
FIELD_ERROR
status type in third party signinup API withGENERAL_ERROR
. - Removed
FIELD_ERROR
status type from third party signinup recipe function.
- Adds
- If sms or email sending failed in passwordless recipe APIs, we now throw a regular JS error from the API as opposed to returning a
GENERAL_ERROR
to the client. - If there is an error whilst getting the profile info about a user from a third party provider (in /signinup POST API), then we throw a regular JS error instead of returning a
GENERAL_ERROR
to the client. - Make email and sms delivery ingredient interfaces developer friendly:
- Remove the need of
SMSDeliveryTwilioConfig
,EmailDeliverySMTPConfig
, andSupertokensServiceConfig
. - Export
(.*)OverrideInput
and(Email|SMS)DeliveryOverrideInput
from the relevant recipes. - Rename
Type<Recipe>EmailDeliveryInput
to<Recipe>EmailTemplateVars
- Export
EmailTemplateVars
(alias of<Recipe>EmailTemplateVars
) from all the relevant recipes - Export
PasswordlessLogin(Email|SMS)TemplateVars
,PasswordResetEmailTemplateVars
, andVerificationEmailTemplateVars
from relevant recipes. - Rename
(.*)ServiceConfig
to(.*)Settings
for readability. - Rename arg
input_
totemplate_vars
inEmailDeliveryInterface.send_email
andSMTPServiceInterface.send_sms
functions. - Rename arg
input_
tocontent
andtemplate_vars
inSMTPServiceInterface.send_raw_email
andSMTPServiceInterface.get_content
functions respectively. - Rename arg
get_content_result
tocontent
andinput_
totemplate_vars
inTwilioServiceInterface.send_raw_email
andTwilioServiceInterface.get_content
functions respectively.
- Remove the need of
- Removes support for FDI < 1.14
- Changes
get_email_for_user_id
function inside thirdpartypasswordless to take into account passwordless emails and return an empty string in case a passwordless email doesn't exist. This helps situations where the dev wants to customise the email verification functions in the thirdpartypasswordless recipe.
email_delivery
user config for Emailpassword, Thirdparty, ThirdpartyEmailpassword, Passwordless and ThirdpartyPasswordless recipes.sms_delivery
user config for Passwordless and ThirdpartyPasswordless recipes.Twilio
service integartion forsms_delivery
ingredient.SMTP
service integration foremail_delivery
ingredient.Supertokens
service integration forsms_delivery
ingredient.
- For Emailpassword recipe input config,
reset_password_using_token_feature.create_and_send_custom_email
andemail_verification_feature.create_and_send_custom_email
have been deprecated. - For Thirdparty recipe input config,
email_verification_feature.create_and_send_custom_email
has been deprecated. - For ThirdpartyEmailpassword recipe input config,
reset_password_using_token_feature.create_and_send_custom_email
andemail_verification_feature.create_and_send_custom_email
have been deprecated. - For Passwordless recipe input config,
create_and_send_custom_email
andcreateAndSendCustomTextMessage
have been deprecated. - For ThirdpartyPasswordless recipe input config,
create_and_send_custom_email
,createAndSendCustomTextMessage
andemail_verification_feature.create_and_send_custom_email
have been deprecated.
Following is an example of ThirdpartyPasswordless recipe migration. If your existing code looks like
from supertokens_python import InputAppInfo, SupertokensConfig, init
from supertokens_python.recipe import thirdpartypasswordless
async def send_pless_login_email(input_: TypePasswordlessEmailDeliveryInput, user_context: Dict[str, Any]):
print("SEND_PLESS_LOGIN_EMAIL", input_.email, input_.user_input_code)
async def send_pless_login_sms(input_: TypeThirdPartyPasswordlessSmsDeliveryInput, user_context: Dict[str, Any]):
print("SEND_PLESS_LOGIN_SMS", input_.phone_number, input_.user_input_code)
async def send_ev_verification_email(user: TpPlessUser, link: str, user_context: Any):
print("SEND_EV_LOGIN_SMS", user.email, user.phone_number, user.third_party_info)
init(
supertokens_config=SupertokensConfig('http://localhost:3567'),
app_info=InputAppInfo(
api_domain="...",
app_name="...",
website_domain="...",
),
framework='...',
recipe_list=[thirdpartypasswordless.init(
contact_config=passwordless.ContactEmailOrPhoneConfig(
create_and_send_custom_email=send_pless_login_email,
create_and_send_custom_text_message=send_pless_login_sms,
),
flow_type='...',
email_verification_feature=thirdpartypasswordless.InputEmailVerificationConfig(
create_and_send_custom_email=send_ev_verification_email,
)
)]
)
After migration to using new email_delivery
and sms_delivery
config, your code would look like:
from supertokens_python import InputAppInfo, SupertokensConfig, init
from supertokens_python.ingredients.emaildelivery.types import EmailDeliveryInterface, EmailDeliveryConfig
from supertokens_python.ingredients.smsdelivery.types import SMSDeliveryInterface, SMSDeliveryConfig
from supertokens_python.recipe import thirdpartypasswordless, passwordless
from supertokens_python.recipe.emailverification.types import TypeEmailVerificationEmailDeliveryInput
async def send_pless_login_email(input_: TypePasswordlessEmailDeliveryInput, user_context: Dict[str, Any]):
print("SEND_PLESS_LOGIN_EMAIL", input_.email, input_.user_input_code)
async def send_pless_login_sms(input_: TypeThirdPartyPasswordlessSmsDeliveryInput, user_context: Dict[str, Any]):
print("SEND_PLESS_LOGIN_SMS", input_.phone_number, input_.user_input_code)
async def send_ev_verification_email(user: TpPlessUser, link: str, user_context: Any):
print("SEND_EV_LOGIN_SMS", user.email, user.phone_number, user.third_party_info)
class EmailDeliveryService(EmailDeliveryInterface):
async def send_email(self, input_: TypeThirdPartyPasswordlessEmailDeliveryInput, user_context: Dict[str, Any]):
if isinstance(input_, TypeEmailVerificationEmailDeliveryInput):
await send_ev_verification_email(input_, user_context)
elif isinstance(input_, TypePasswordlessEmailDeliveryInput):
await send_pless_login_email(input_, user_context)
class SMSDeliveryService(SMSDeliveryInterface):
async def send_sms(self, input_: TypeThirdPartyPasswordlessSmsDeliveryInput, user_context: Dict[str, Any]):
await send_pless_login_sms(input_, user_context)
init(
supertokens_config=SupertokensConfig('http://localhost:3567'),
app_info=InputAppInfo(
app_name="...",
api_domain="...",
website_domain="...",
),
framework='...',
recipe_list=[thirdpartypasswordless.init(
contact_config=passwordless.ContactEmailOrPhoneConfig(),
flow_type='...',
email_delivery=EmailDeliveryConfig(
service=EmailDeliveryService(),
),
sms_delivery=SMSDeliveryConfig(
service=SMSDeliveryService(),
),
)]
)
- Fix bugs in syncio functions across all the recipes
- Fixes bug in resend code POST API in passwordless recipe to use the correct instance type during checks.
- Fixes bug in thirdpartypasswordless recipe to prevent infinite loop during resent code API
- Update phonenumbers lib dependency version
- Adds type checks to the parameters of the emailpassword init funtion.
- Adds type checks to the parameters of the emailverification init funtion.
- Adds type checks to the parameters of the jwt init funtion.
- Adds type checks to the parameters of the openid init funtion.
- Adds type checks to the parameters of the session init funtion.
- Adds type checks to the parameters of the passwordless init funtion.
- Adds type checks to the parameters of the thirdparty init funtion.
- Adds type checks to the parameters of the thirdpartyemailpassword init funtion.
- Adds type checks to the parameters of the thirdpartypasswordless init funtion.
- Adds type checks to the parameters of the usermetadata init funtion.
- Adds django with thirdpartyemailpassword example.
- Fixed execute_async to check and use asyncio mode.
- Ignores any exception from send_telemetry, not to prevent the app from starting up.
- Updates
RecipeInterface
andAPIInterface
methods to return exact return types instead of abstract base types, for the emailpassword recipe. - Updates
RecipeInterface
andAPIInterface
methods to return exact return types instead of abstract base types, for the thirdparty recipe. - Updates
RecipeInterface
andAPIInterface
methods to return exact return types instead of abstract base types, for the passwordless recipe. - Updates
RecipeInterface
andAPIInterface
methods to return exact return types instead of abstract base types, for the openid recipe. - Updates
RecipeInterface
andAPIInterface
methods to return exact return types instead of abstract base types, for the JWT recipe. - Updates
RecipeInterface
andAPIInterface
methods to return exact return types instead of abstract base types, for the session recipe. - Updates
RecipeInterface
methods to return exact return types instead of abstract base types, for the usermetadata recipe. - Adds
EmailPasswordSignInOkResult
,EmailPasswordSignUpOkResult
andThirdPartySignInUpOkResult
to use the thirdpartyemailpassword recipe'sUser
class. - Adds
ThirdPartySignInUpPostOkResult
,EmailPasswordSignInPostOkResult
andEmailPasswordSignUpPostOkResult
to use the thirdpartyemailpassword recipe'sUser
class. - Renames wrongly named
ResetPasswordUsingTokenWrongUserIdErrorResult
toResetPasswordUsingTokenInvalidTokenError
, one of the return types ofreset_password_using_token
method in theRecipeInterface
. - Removes unused classes
GeneratePasswordResetTokenResponse
,EmailExistsResponse
andPasswordResetResponse
. - Removed
third_party_info
from emailpasswordUser
class. - Exports re-used Result and Response classes from
thirdparty
&emailpassword
recipe interfaces in thethirdpartyemailpassword
recipe interfaces. - Exports re-used Result and Response classes from
thirdparty
&passwordless
recipe interfaces in thethirdpartypasswordless
recipe interfaces. - Renames
*ErrorResult
classes to*Error
. - Renames
*ErrorResponse
classes to*Error
. - Renames
*OkResponse
classes to*OkResult
. - Renames
*ResultOk
classes to*OkResult
.
- Fixed execute_async to check and use asyncio mode.
- Ignores any exception from send_telemetry, not to prevent the app from starting up.
- Bug fix in telemetry data API
- Updates Project Setup, Modifying Code and Testing sections in the contributing guide
- Fixed async execution of
send_telemetry
in init andcall_get_handshake_info
in session recipe implementation. - Fixed
Content-length
in FastAPI Response wrapper.
- Changes third party provider type to get client ID dynamically so that it can be changed based on user context.
- Adds delete email (
delete_email_for_user
) and phone number (delete_phone_number_for_user
) functions for passwordless and thirdpartypasswordless recipe - Adds check for user type in update passwordless info in thirdpartypasswordless recipe
- Fixes issue in user metadata recipe where as are exposing async functions in the syncio file.
- Upgrade and freeze pyright version
- Rename
compare_version
toget_max_version
for readability - Add user metadata recipe
- bug fix in
default_create_and_send_custom_email
for emailverification recipe where we were not sending the email if env var was not set. - Fix telemetry issues related to asyncio when using FastAPI. Related issue: supertokens/supertokens-core#421
- adds git action for running tests
- Setup logging for easier debugging
- Adds github action for checking all things checked by pre commit hook
- Fix Passwordless OTP recipe phone number field to fix supertokens/supertokens-core#416
- Expands allowed version range for httpx library to fix #98
- Removes dependency on framework specific dependencies (
werkzeug
andstarlette
)
- Import for fastapi middleware:
- Old
from supertokens_python.framework.fastapi import Middleware app = FastAPI() app.add_middleware(Middleware)
- New
from supertokens_python.framework.fastapi import get_middleware app = FastAPI() app.add_middleware(get_middleware())
- Old
user_context
was passed incorrectly to thecreate_new_session_function
.
- Bug in user pagination functions: #95
- #90
- Thirdpartypasswordless recipe + tests
- Added new function to BaseRequest class called
set_session_as_none
to set session object to None.
- Bug where a user had to add dependencies on all frameworks when using the SDK: #82
- User context property added for all recipes' apis and functions
- Removes deprecated functions in recipe for user pagination and user count
- Changed email verification input functions' user type in emailpassword to be equal to emailpassword's user and not emailverification user.
- All session recipe's error handler not need to return
BaseResponse
. - Session's recipe
get_session_information
returns aSessionInformationResult
class object instead of adict
for easier consumption. get_link_domain_and_path
config in passwordless recipe now takes a class type input as opposed to a string input as the first param- Renamed
Session
toSessionContainer
in session - Upgrades
typing_extensions
to version 4.1.1 - Renames functions in ThirdPartyEmailPassword recipe (supertokens/supertokens-node#219):
- Recipe Interface:
sign_in_up
->thirdparty_sign_in_up
sign_up
->emailpassword_sign_up
sign_in
->emailpassword_sign_in
- API Interface:
email_exists_get
->emailpassword_email_exists_get
- User exposed functions (in
recipe/thirdpartyemailpassword/asyncio
andrecipe/thirdpartyemailpassword/syncio
)sign_in_up
->thirdparty_sign_in_up
sign_up
->emailpassword_sign_up
sign_in
->emailpassword_sign_in
- Recipe Interface:
- Returns session from all APIs where a session is created
- Added
regenerate_access_token
as a new recipe function for the session recipe. - Strong typings.
- Changed async_to_sync_wrapper.py file to make it simpler
- Remove default
= None
for functions internal to the package
- If logging in via social login and the email is already verified from the provider's side, it marks the email as verified in SuperTokens core.
- Corrects how override is done in thirdpartyemailpassword recipe and API implementation
- add workflow to verify if pr title follows conventional commits
- Added userId as an optional property to the response of
recipe/user/password/reset
(compatibility with CDI 2.12). - Adds ability to give a path for each of the hostnames in the connectionURI: supertokens/supertokens-node#252
- Bug fixes in Literal import which caused issues when using the sdk with python version 3.7.
- Fixes supertokens/supertokens-node#244 - throws an error if a user tries to update email / password of a third party login user.
- Adds passwordless recipe
- Adds compatibility with FDI 1.12 and CDI 2.11
- Bug in ThirdpartyEmailpassword recipe init function when InputSignUpFeature is not passed.
- delete_user function
- Compatibility with CDI 2.10
- Config changes
- Added
mode
config for FastAPI which now supports bothasgi
andwsgi
. - The ability to enable JWT creation with session management, this allows easier integration with services that require JWT based authentication: supertokens/supertokens-core#250
- You can do BaseRequest.request to get the original request object. Fixes #61
- Removes use of apiGatewayPath from apple's redirect URI since that is already there in the apiBasePath
- Sign in with Discord, Google workspaces.
- Allow sending of custom response: supertokens/supertokens-node#197
- Change
set_content
toset_json_content
in all the frameworks - Adds
"application/json; charset=utf-8"
header to json responses.
- When routing, ignores
rid
value"anti-csrf"
: #54 get_redirect_uri
function added to social providers in case we set theredirect_uri
on the backend.- Adds optional
is_default
param to auth providers so that they can be reused with different credentials. - Verifies ID Token sent for sign in with apple as per https://developer.apple.com/documentation/sign_in_with_apple/sign_in_with_apple_rest_api/verifying_a_user
- Removes empty awslambda folder from framework
- If json parsing fails in the frameworks, catches those exceptions and returns an empty object.
- Removes
sign_in_up_post
from thirdpartyemailpassword API interface and replaces it with three APIs:email_password_sign_in_post
,email_password_sign_up_post
andthird_party_sign_in_up_post
: supertokens/supertokens-node#192 - Renames all "jwt" related functions in session recipe to use "access_token" instead
- jwt recipe and unit tests
- Support for FDI 1.10: Allow thirdparty
/signinup POST
API to takeauthCodeResponse
XORcode
so that it can supprt OAuth via PKCE - Apple provider disabled for now
- all the user facing async functions now needs to be imported from asyncio sub directory. For example, importing the async implementation of create_new_session from session recipe has changed from:
to:
from supertokens_python.recipe.session import create_new_session
from supertokens_python.recipe.session.asyncio import create_new_session
- sync versions of the functions are now needs to be imported from syncio directory instead of the sync directory
- all the license comments now uses single line comment structure instead of multi-line comment structure
- auth-react tests for flask and django
- if running django in async way, set
mode
toasgi
inconfig
- Adds OAuth development keys for Google and Github for faster recipe implementation.
- Removed the Literal from python 3.8 and added Literal from typing_extensions package. Now supertokens_python can be used with python 3.7 .
- dependency issues for frameworks
- Multiple framework support. Currently supporting Django, Flask(1.x) and Fastapi.
- BaseRequest and BaseResponse interfaces which are used inside recipe instead of previously used Response and Request from Fastapi.
- Middleware, error handlers and verify session for each framework.
- Created a wrapper for async to sync for supporting older version of python web frameworks.
- Base tests for each framework.
- New requirements in the setup file.