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.
- Adds support for OAuth2
- Added new feature in license key:
OAUTH
- Adds new core config:
oauth_provider_public_service_url
oauth_provider_admin_service_url
oauth_provider_consent_login_base_url
oauth_provider_url_configured_in_oauth_provider
- Adds following APIs:
- POST
/recipe/oauth/clients
- PUT
/recipe/oauth/clients
- GET
/recipe/oauth/clients
- GET
/recipe/oauth/clients/list
- POST
/recipe/oauth/clients/remove
- GET
/recipe/oauth/auth/requests/consent
- PUT
/recipe/oauth/auth/requests/consent/accept
- PUT
/recipe/oauth/auth/requests/consent/reject
- GET
/recipe/oauth/auth/requests/login
- PUT
/recipe/oauth/auth/requests/login/accept
- PUT
/recipe/oauth/auth/requests/login/reject
- GET
/recipe/oauth/auth/requests/logout
- PUT
/recipe/oauth/auth/requests/logout/accept
- PUT
/recipe/oauth/auth/requests/logout/reject
- POST
/recipe/oauth/auth
- POST
/recipe/oauth/token
- POST
/recipe/oauth/introspect
- POST
/recipe/oauth/session/revoke
- POST
/recipe/oauth/token/revoke
- POST
/recipe/oauth/tokens/revoke
- POST
- Added new feature in license key:
If using PostgreSQL, run the following SQL script:
CREATE TABLE IF NOT EXISTS oauth_clients (
app_id VARCHAR(64),
client_id VARCHAR(255) NOT NULL,
is_client_credentials_only BOOLEAN NOT NULL,
PRIMARY KEY (app_id, client_id),
FOREIGN KEY(app_id) REFERENCES apps(app_id) ON DELETE CASCADE
);
CREATE TABLE IF NOT EXISTS oauth_sessions (
gid VARCHAR(255),
app_id VARCHAR(64) DEFAULT 'public',
client_id VARCHAR(255) NOT NULL,
session_handle VARCHAR(128),
external_refresh_token VARCHAR(255) UNIQUE,
internal_refresh_token VARCHAR(255) UNIQUE,
jti TEXT NOT NULL,
exp BIGINT NOT NULL,
PRIMARY KEY (gid),
FOREIGN KEY(app_id, client_id) REFERENCES oauth_clients(app_id, client_id) ON DELETE CASCADE
);
CREATE INDEX IF NOT EXISTS oauth_session_exp_index ON oauth_sessions(exp DESC);
CREATE INDEX IF NOT EXISTS oauth_session_external_refresh_token_index ON oauth_sessions(app_id, external_refresh_token DESC);
CREATE TABLE IF NOT EXISTS oauth_m2m_tokens (
app_id VARCHAR(64) DEFAULT 'public',
client_id VARCHAR(255) NOT NULL,
iat BIGINT NOT NULL,
exp BIGINT NOT NULL,
PRIMARY KEY (app_id, client_id, iat),
FOREIGN KEY(app_id, client_id) REFERENCES oauth_clients(app_id, client_id) ON DELETE CASCADE
);
CREATE INDEX IF NOT EXISTS oauth_m2m_token_iat_index ON oauth_m2m_tokens(iat DESC, app_id DESC);
CREATE INDEX IF NOT EXISTS oauth_m2m_token_exp_index ON oauth_m2m_tokens(exp DESC);
CREATE TABLE IF NOT EXISTS oauth_logout_challenges (
app_id VARCHAR(64) DEFAULT 'public',
challenge VARCHAR(128) NOT NULL,
client_id VARCHAR(255) NOT NULL,
post_logout_redirect_uri VARCHAR(1024),
session_handle VARCHAR(128),
state VARCHAR(128),
time_created BIGINT NOT NULL,
PRIMARY KEY (app_id, challenge),
FOREIGN KEY(app_id, client_id) REFERENCES oauth_clients(app_id, client_id) ON DELETE CASCADE
);
CREATE INDEX IF NOT EXISTS oauth_logout_challenges_time_created_index ON oauth_logout_challenges(time_created DESC);
If using MySQL, run the following SQL script:
CREATE TABLE IF NOT EXISTS oauth_clients (
app_id VARCHAR(64),
client_id VARCHAR(255) NOT NULL,
is_client_credentials_only BOOLEAN NOT NULL,
PRIMARY KEY (app_id, client_id),
FOREIGN KEY(app_id) REFERENCES apps(app_id) ON DELETE CASCADE
);
CREATE TABLE IF NOT EXISTS oauth_sessions (
gid VARCHAR(255),
app_id VARCHAR(64) DEFAULT 'public',
client_id VARCHAR(255) NOT NULL,
session_handle VARCHAR(128),
external_refresh_token VARCHAR(255) UNIQUE,
internal_refresh_token VARCHAR(255) UNIQUE,
jti TEXT NOT NULL,
exp BIGINT NOT NULL,
PRIMARY KEY (gid),
FOREIGN KEY(app_id, client_id) REFERENCES oauth_clients(app_id, client_id) ON DELETE CASCADE
);
CREATE INDEX oauth_session_exp_index ON oauth_sessions(exp DESC);
CREATE INDEX oauth_session_external_refresh_token_index ON oauth_sessions(app_id, external_refresh_token DESC);
CREATE TABLE oauth_m2m_tokens (
app_id VARCHAR(64) DEFAULT 'public',
client_id VARCHAR(255) NOT NULL,
iat BIGINT UNSIGNED NOT NULL,
exp BIGINT UNSIGNED NOT NULL,
PRIMARY KEY (app_id, client_id, iat),
FOREIGN KEY(app_id, client_id) REFERENCES oauth_clients(app_id, client_id) ON DELETE CASCADE
);
CREATE INDEX oauth_m2m_token_iat_index ON oauth_m2m_tokens(iat DESC, app_id DESC);
CREATE INDEX oauth_m2m_token_exp_index ON oauth_m2m_tokens(exp DESC);
CREATE TABLE IF NOT EXISTS oauth_logout_challenges (
app_id VARCHAR(64) DEFAULT 'public',
challenge VARCHAR(128) NOT NULL,
client_id VARCHAR(255) NOT NULL,
post_logout_redirect_uri VARCHAR(1024),
session_handle VARCHAR(128),
state VARCHAR(128),
time_created BIGINT UNSIGNED NOT NULL,
PRIMARY KEY (app_id, challenge),
FOREIGN KEY(app_id, client_id) REFERENCES oauth_clients(app_id, client_id) ON DELETE CASCADE
);
CREATE INDEX oauth_logout_challenges_time_created_index ON oauth_logout_challenges(time_created ASC, app_id ASC);
- Adds support for
--with-temp-dir
in CLI andtempDirLocation=
in Core - Adds validation to firstFactors and requiredSecondaryFactors names while creating tenants/apps/etc. to not allow special chars.
- Adds index on
last_active_time
foruser_last_active
table to improve the performance of MAU computation.
If using PostgreSQL, run the following SQL script:
CREATE INDEX IF NOT EXISTS user_last_active_last_active_time_index ON user_last_active (last_active_time DESC, app_id DESC);
If using MySQL, run the following SQL script:
CREATE INDEX user_last_active_last_active_time_index ON user_last_active (last_active_time DESC, app_id DESC);
- Removes the stats that were resulting in high CPU consumption
- Adds
SECURITY
feature inEE_FEATURES
.
- Fixes path routing which rejected tenantId stop words even if it was not an exact stop word match. For example,
/hellotenant
is a valid tenantId prefix, however, it was being rejected for the stop wordhello
. - #1021 - 500 errors in core returns actual exception, since these APIs are developer facing, it makes easier to debug these errors.
- Account linking now properly checks if the login methods of the primary user can be shared with the tenants of the recipe user we are trying to link
- Simplifying email verification token creation
- Adds support for CDI 3.1 and 5.1
- Adds annotations to properties
CoreConfig
to aid dashboard API. - Updates
ApiVersionAPI
to optionally acceptwebsiteDomain
andapiDomain
for telemetry. - Adds GET
/recipe/dashboard/tenant/core-config
to fetch the core properties with metadata for dashboard. - Reports
websiteDomain
andapiDomain
for each app in telemetry. - API Key can now be passed using the
Authorization
header:Authorization: <api-key>
-
CUD/App/Tenant Management APIs are deprecated and v2 versions have been added
- Adds new core API for fetching all the core properties for a tenant
- GET
/appid-<appid>/<tenantid>/recipe/dashboard/tenant/core-config
- GET
- Deprecated the following APIs
- PUT
/recipe/multitenancy/connectionuridomain
- GET
/recipe/multitenancy/connectionuridomain/list
- PUT
/recipe/multitenancy/app
- GET
/recipe/multitenancy/app/list
- PUT
/appid-<appid>/recipe/multitenancy/tenant
- GET
/appid-<appid>/<tenantid>/recipe/multitenancy/tenant
- GET
/appid-<appid>/<tenantid>/recipe/multitenancy/tenant/list
- PUT
- Adds the following APIs to replace the deprecated APIs
- PUT
/recipe/multitenancy/connectionuridomain/v2
- GET
/recipe/multitenancy/connectionuridomain/list/v2
- PUT
/recipe/multitenancy/app/v2
- GET
/recipe/multitenancy/app/list/v2
- PUT
/appid-<appid>/recipe/multitenancy/tenant/v2
- GET
/appid-<appid>/<tenantid>/recipe/multitenancy/tenant/v2
- GET
/appid-<appid>/<tenantid>/recipe/multitenancy/tenant/list/v2
- PUT
- Adds new core API for fetching all the core properties for a tenant
-
In CDI 5.1, the auth recipe APIs such as emailpassword signIn, thirdParty signInUp, etc would not be blocked if the recipe was disabled using the deprecated APIs. They will be enforced if CDI version <= 5.0 is being passed in the header.
- Updates descriptions in the config.yaml to be consistent with the annotations.
- Adds correct
max-age
forJWKSPublicAPI
based on dynamic key generation interval. - Fixes
500
error when using TOTP code longer than 8 characters.
Make sure the core is already upgraded to version 9.0.2 before migrating
If using PostgreSQL
ALTER TABLE tenant_configs ADD COLUMN IF NOT EXISTS is_first_factors_null BOOLEAN DEFAULT TRUE;
ALTER TABLE tenant_configs ALTER COLUMN is_first_factors_null DROP DEFAULT;
If using MySQL
ALTER TABLE tenant_configs ADD COLUMN is_first_factors_null BOOLEAN DEFAULT TRUE;
ALTER TABLE tenant_configs ALTER COLUMN is_first_factors_null DROP DEFAULT;
- Fixes issue with core startup when creation of CUD/app/tenant has partial failure
- Fixes verify TOTP and verify device APIs to treat any code as invalid
- Fixes the computation of the number of failed attempts when return
INVALID_TOTP_ERROR
- Supports CDI version
5.0
- MFA stats in
EEFeatureFlag
- Adds
ImportTotpDeviceAPI
- Adds
CheckCodeAPI
deviceName
in request body ofCreateOrUpdateTotpDeviceAPI
POST
is now optional- Adds
firstFactors
andrequiredSecondaryFactors
in request body of create or update CUD, App and Tenant APIs - Adds
deviceName
in the response ofCreateOrUpdateTotpDeviceAPI
POST
VerifyTOTPAPI
changes- Removes
allowUnverifiedDevices
from request body and unverified devices are not allowed - Adds
currentNumberOfFailedAttempts
andmaxNumberOfFailedAttempts
in response when status isINVALID_TOTP_ERROR
orLIMIT_REACHED_ERROR
- Adds status
UNKNOWN_USER_ID_ERROR
- Removes
VerifyTotpDeviceAPI
changes- Adds
currentNumberOfFailedAttempts
andmaxNumberOfFailedAttempts
in response when status isINVALID_TOTP_ERROR
orLIMIT_REACHED_ERROR
- Adds
- Adds
consumedDevice
in the success response of theConsumeCodeAPI
- Adds
preAuthSessionId
input toDeleteCodeAPI
to be able to delete codes for a device - Adds a new
useDynamicSigningKey
into the request body ofRefreshSessionAPI
- This enables smooth switching between
useDynamicAccessTokenSigningKey
settings by allowing refresh calls to change the signing key type of a session - This is available after CDI3.0
- This is required in&after CDI5.0 and optional before
- This enables smooth switching between
- Adds optional
firstFactors
andrequiredSecondaryFactors
to the create or update connectionUriDomain, app and tenant APIs - Updates Last active while linking accounts
- Marks fake email in email password sign up as verified
- Fixes slow down in useridmapping queries
- Adds core version in the logs
- Fixes issue with session creation when using external user id on a linked account
- Enforces the API call from public tenant for the APIs that are app specific
Make sure the core is already upgraded to version 8.0.0 before migrating
If using PostgreSQL
ALTER TABLE totp_user_devices
ADD COLUMN IF NOT EXISTS created_at BIGINT default 0;
ALTER TABLE totp_user_devices
ALTER COLUMN created_at DROP DEFAULT;
If using MySQL
ALTER TABLE totp_user_devices
ADD COLUMN created_at BIGINT UNSIGNED default 0;
ALTER TABLE totp_user_devices
ALTER COLUMN created_at DROP DEFAULT;
DROP INDEX all_auth_recipe_users_pagination_index2 ON all_auth_recipe_users;
DROP INDEX all_auth_recipe_users_pagination_index4 ON all_auth_recipe_users;
- Making this version backward compatible. Breaking changes in
8.0.0
can now be ignored.
-
The following app specific APIs return a 403 when they are called with a tenant ID other than the
public
one. For example, if the path is/users/count/active
, and you call it with/tenant1/users/count/active
, it will return a 403. But if you call it with/public/users/count/active
, or just/users/count/active
, it will work.- GET
/recipe/accountlinking/user/primary/check
- GET
/recipe/accountlinking/user/link/check
- POST
/recipe/accountlinking/user/primary
- POST
/recipe/accountlinking/user/link
- POST
/recipe/accountlinking/user/unlink
- GET
/users/count/active
- POST
/user/remove
- GET
/ee/featureflag
- GET
/user/id
- PUT
/ee/license
- DELETE
/ee/license
- GET
/ee/license
- GET
/requests/stats
- GET
/recipe/user
when querying byuserId
- GET
/recipe/jwt/jwks
- POST
/recipe/jwt
- GET
- Fixes issue with non-auth recipe related storage handling
For Postgresql:
ALTER TABLE user_roles DROP CONSTRAINT IF EXISTS user_roles_role_fkey;
For MySQL:
ALTER TABLE user_roles DROP FOREIGN KEY user_roles_ibfk_1;
ALTER TABLE user_roles DROP FOREIGN KEY user_roles_ibfk_2;
ALTER TABLE user_roles
ADD FOREIGN KEY (app_id, tenant_id)
REFERENCES tenants (app_id, tenant_id) ON DELETE CASCADE;
- Fixes vulnerabilities in dependencies
- Updates telemetry payload
- Fixes Active User tracking to use the right storage
- Fixes issue where error logs were printed to StdOut instead of StdErr.
- Adds new config
supertokens_saas_load_only_cud
that makes the core instance load a particular CUD only, irrespective of the CUDs present in the db. - Fixes connection pool handling when connection pool size changes for a tenant.
- Returns 400, instead of 500, for badly typed core config while creating CUD, App or Tenant
- Adds test for user pagination from old version
- Updates test user query speed
If using MySQL plugin, run the following SQL script:
CREATE INDEX app_id_to_user_id_primary_user_id_index ON app_id_to_user_id (primary_or_recipe_user_id);
CREATE INDEX app_id_to_user_id_user_id_index ON app_id_to_user_id (user_id);
- Adds test to user query speed
If using PostgreSQL database, run the following sql script:
CREATE INDEX IF NOT EXISTS app_id_to_user_id_primary_user_id_index ON app_id_to_user_id (primary_or_recipe_user_id, app_id);
In this release, the core API routes have been updated to incorporate phone number normalization before processing. Consequently, existing entries in the database also need to undergo normalization. To facilitate this, we have included a migration script to normalize phone numbers for all the existing entries.
NOTE: You can skip the migration if you are not using passwordless via phone number.
This script updates the phone_number
column in the passwordless_users
, passwordless_user_to_tenant
,
and passwordless_devices
tables with their respective normalized values. This script is idempotent and can be run
multiple times without any issue. Follow the steps below to run the script:
-
Ensure that the core is already upgraded to version 7.0.12 (CDI version 4.0)
-
Run the migration script
Make sure your Node.js version is 16 or above to run the script. Locate the migration script at
supertokens-core/migration_scripts/to_version_7_0_12/index.js
. Modify the script by updating theDB_HOST
,DB_USER
,DB_PASSWORD
, andDB_NAME
variables with the correct values. Subsequently, run the following commands to initiate the script:$ git clone https://github.com/supertokens/supertokens-core.git $ cd supertokens-core/migration_scripts/to_version_7_0_12 $ npm install $ npm start
Performance Note: On average, the script takes 19s for every 1000 rows with a maximum of 1 connection, 4.7s with a maximum of 5 connections (default), and 4.5s with a maximum of 10 connections. Increasing the
MAX_POOL_SIZE
allows the script to leverage more connections simultaneously, potentially improving execution speed.
- Fixes email verification behaviour with user id mapping
- Collects requests stats per app
- Adds
/requests/stats
API to return requests stats for the last day
- Tests
verified
inloginMethods
for users with userId mapping
- Tests thirdParty serialization fix
- Fixes test that verifies tenant config persistence
- Fixes issue with cron tasks that run per app and tenant
- Adds postgres testing to the CICD
- Fixes user info from primary user id query
- Fixes
deviceIdHash
issue
- Fixes issue with duplicate cron task
- Fixes
500
error for passwordless login in certain cases - #828
- Remove padding from link codes and pre-auth session ids in passwordless, but keep support for old format that included
padding (
=
signs)
- Support for CDI version 4.0
- Adds Account Linking feature
- New access token version: v5, which contains a required prop:
rsub
. This contains the recipe user ID that belongs to the login method that the user used to login. Thesub
claim in the access token payload is now the primary user ID. - APIs that return
SessionInformation
(like GET/recipe/session
) contains userId, recipeUserId in the response. - Apis that create / modify / refresh a session return the
recipeUserId
in thesession
object in the response. - Token theft detected response returns userId and recipeUserId
- Adds columns
primary_or_recipe_user_id
,is_linked_or_is_a_primary_user
andprimary_or_recipe_user_time_joined
toall_auth_recipe_users
table - Adds columns
primary_or_recipe_user_id
andis_linked_or_is_a_primary_user
toapp_id_to_user_id
table - Removes index
all_auth_recipe_users_pagination_index
and addesall_auth_recipe_users_pagination_index1
,all_auth_recipe_users_pagination_index2
,all_auth_recipe_users_pagination_index3
andall_auth_recipe_users_pagination_index4
indexes instead onall_auth_recipe_users
table - Adds
all_auth_recipe_users_recipe_id_index
onall_auth_recipe_users
table - Adds
all_auth_recipe_users_primary_user_id_index
onall_auth_recipe_users
table - Adds
email
column toemailpassword_pswd_reset_tokens
table - Changes
user_id
foreign key constraint onemailpassword_pswd_reset_tokens
toapp_id_to_user_id
table
-
Ensure that the core is already upgraded to version 6.0.13 (CDI version 3.0)
-
Stop the core instance(s)
-
Run the migration script
If using PostgreSQL
ALTER TABLE all_auth_recipe_users ADD COLUMN primary_or_recipe_user_id CHAR(36) NOT NULL DEFAULT ('0'); ALTER TABLE all_auth_recipe_users ADD COLUMN is_linked_or_is_a_primary_user BOOLEAN NOT NULL DEFAULT FALSE; ALTER TABLE all_auth_recipe_users ADD COLUMN primary_or_recipe_user_time_joined BIGINT NOT NULL DEFAULT 0; UPDATE all_auth_recipe_users SET primary_or_recipe_user_id = user_id WHERE primary_or_recipe_user_id = '0'; UPDATE all_auth_recipe_users SET primary_or_recipe_user_time_joined = time_joined WHERE primary_or_recipe_user_time_joined = 0; ALTER TABLE all_auth_recipe_users ADD CONSTRAINT all_auth_recipe_users_primary_or_recipe_user_id_fkey FOREIGN KEY (app_id, primary_or_recipe_user_id) REFERENCES app_id_to_user_id (app_id, user_id) ON DELETE CASCADE; ALTER TABLE all_auth_recipe_users ALTER primary_or_recipe_user_id DROP DEFAULT; ALTER TABLE app_id_to_user_id ADD COLUMN primary_or_recipe_user_id CHAR(36) NOT NULL DEFAULT ('0'); ALTER TABLE app_id_to_user_id ADD COLUMN is_linked_or_is_a_primary_user BOOLEAN NOT NULL DEFAULT FALSE; UPDATE app_id_to_user_id SET primary_or_recipe_user_id = user_id WHERE primary_or_recipe_user_id = '0'; ALTER TABLE app_id_to_user_id ADD CONSTRAINT app_id_to_user_id_primary_or_recipe_user_id_fkey FOREIGN KEY (app_id, primary_or_recipe_user_id) REFERENCES app_id_to_user_id (app_id, user_id) ON DELETE CASCADE; ALTER TABLE app_id_to_user_id ALTER primary_or_recipe_user_id DROP DEFAULT; DROP INDEX all_auth_recipe_users_pagination_index; CREATE INDEX all_auth_recipe_users_pagination_index1 ON all_auth_recipe_users ( app_id, tenant_id, primary_or_recipe_user_time_joined DESC, primary_or_recipe_user_id DESC); CREATE INDEX all_auth_recipe_users_pagination_index2 ON all_auth_recipe_users ( app_id, tenant_id, primary_or_recipe_user_time_joined ASC, primary_or_recipe_user_id DESC); CREATE INDEX all_auth_recipe_users_pagination_index3 ON all_auth_recipe_users ( recipe_id, app_id, tenant_id, primary_or_recipe_user_time_joined DESC, primary_or_recipe_user_id DESC); CREATE INDEX all_auth_recipe_users_pagination_index4 ON all_auth_recipe_users ( recipe_id, app_id, tenant_id, primary_or_recipe_user_time_joined ASC, primary_or_recipe_user_id DESC); CREATE INDEX all_auth_recipe_users_primary_user_id_index ON all_auth_recipe_users (primary_or_recipe_user_id, app_id); CREATE INDEX all_auth_recipe_users_recipe_id_index ON all_auth_recipe_users (app_id, recipe_id, tenant_id); ALTER TABLE emailpassword_pswd_reset_tokens DROP CONSTRAINT IF EXISTS emailpassword_pswd_reset_tokens_user_id_fkey; ALTER TABLE emailpassword_pswd_reset_tokens ADD CONSTRAINT emailpassword_pswd_reset_tokens_user_id_fkey FOREIGN KEY (app_id, user_id) REFERENCES app_id_to_user_id (app_id, user_id) ON DELETE CASCADE; ALTER TABLE emailpassword_pswd_reset_tokens ADD COLUMN email VARCHAR(256);
If using MySQL
ALTER TABLE all_auth_recipe_users ADD primary_or_recipe_user_id CHAR(36) NOT NULL DEFAULT ('0'); ALTER TABLE all_auth_recipe_users ADD is_linked_or_is_a_primary_user BOOLEAN NOT NULL DEFAULT FALSE; ALTER TABLE all_auth_recipe_users ADD primary_or_recipe_user_time_joined BIGINT UNSIGNED NOT NULL DEFAULT 0; UPDATE all_auth_recipe_users SET primary_or_recipe_user_id = user_id WHERE primary_or_recipe_user_id = '0'; UPDATE all_auth_recipe_users SET primary_or_recipe_user_time_joined = time_joined WHERE primary_or_recipe_user_time_joined = 0; ALTER TABLE all_auth_recipe_users ADD FOREIGN KEY (app_id, primary_or_recipe_user_id) REFERENCES app_id_to_user_id (app_id, user_id) ON DELETE CASCADE; ALTER TABLE all_auth_recipe_users ALTER primary_or_recipe_user_id DROP DEFAULT; ALTER TABLE app_id_to_user_id ADD primary_or_recipe_user_id CHAR(36) NOT NULL DEFAULT ('0'); ALTER TABLE app_id_to_user_id ADD is_linked_or_is_a_primary_user BOOLEAN NOT NULL DEFAULT FALSE; UPDATE app_id_to_user_id SET primary_or_recipe_user_id = user_id WHERE primary_or_recipe_user_id = '0'; ALTER TABLE app_id_to_user_id ADD FOREIGN KEY (app_id, primary_or_recipe_user_id) REFERENCES app_id_to_user_id (app_id, user_id) ON DELETE CASCADE; ALTER TABLE app_id_to_user_id ALTER primary_or_recipe_user_id DROP DEFAULT; DROP INDEX all_auth_recipe_users_pagination_index ON all_auth_recipe_users; CREATE INDEX all_auth_recipe_users_pagination_index1 ON all_auth_recipe_users ( app_id, tenant_id, primary_or_recipe_user_time_joined DESC, primary_or_recipe_user_id DESC); CREATE INDEX all_auth_recipe_users_pagination_index2 ON all_auth_recipe_users ( app_id, tenant_id, primary_or_recipe_user_time_joined ASC, primary_or_recipe_user_id DESC); CREATE INDEX all_auth_recipe_users_pagination_index3 ON all_auth_recipe_users ( recipe_id, app_id, tenant_id, primary_or_recipe_user_time_joined DESC, primary_or_recipe_user_id DESC); CREATE INDEX all_auth_recipe_users_pagination_index4 ON all_auth_recipe_users ( recipe_id, app_id, tenant_id, primary_or_recipe_user_time_joined ASC, primary_or_recipe_user_id DESC); CREATE INDEX all_auth_recipe_users_primary_user_id_index ON all_auth_recipe_users (primary_or_recipe_user_id, app_id); CREATE INDEX all_auth_recipe_users_recipe_id_index ON all_auth_recipe_users (app_id, recipe_id, tenant_id); ALTER TABLE emailpassword_pswd_reset_tokens DROP FOREIGN KEY emailpassword_pswd_reset_tokens_ibfk_1; ALTER TABLE emailpassword_pswd_reset_tokens ADD FOREIGN KEY (app_id, user_id) REFERENCES app_id_to_user_id (app_id, user_id) ON DELETE CASCADE; ALTER TABLE emailpassword_pswd_reset_tokens ADD email VARCHAR(256);
-
Start the new instance(s) of the core (version 7.0.0)
- Fixes paid stats reporting for multitenancy
- Fixes randomly occurring
serialization error for concurrent update
inverifySession
API - Fixes
MISSING_EE_FOLDER_ERROR
error when the core starts up with an empty database
- Fixed feature flag cron job
- Fixed an encoding/decoding issue for certain access token payloads
- Now using decimal notation to add numbers into the access token payload (instead of scientific notation)
- Fixes CUD validation starting with number.
- Fixes session removing for user with useridmapping when disassociating from tenant.
- Fixes issue with access token migration from version v1 and v2
- Adds all ee features enabled for in memory database.
- Fixes logging issue in API call where it used to print out the root CUD tenant info when querying with a tenant that does not exist.
- Fixes tenant prefix in stack trace log
supertokens_default_cdi_version
config renamed tosupertokens_max_cdi_version
- Fixes
/apiversion
GET to return versions untilsupertokens_max_cdi_version
if set - Fixes
/recipe/multitenancy/tenant
GET to returnTENANT_NOT_FOUND_ERROR
with 200 status when tenant was not found
- Fixes duplicate users in users search queries when user is associated to multiple tenants
- Fixes wrong tenant id in logging for
APIKeyUnauthorisedException
- Fixes some of the session APIs to return
tenantId
- argon and bcrypt related configs are now configurable only from config.yaml
ip_allow_regex
andip_deny_regex
are now protected properties for SaaShello
is disallowed as a tenantId- creation of apps enables all recipes by default but not during creation of tenant
- Fixes
Invalid API key
issue on hello API - Fixes
CreateOrUpdateThirdPartyConfigAPI
as per CDI 3.0 - Fixes
sessionHandle
to include tenant information and the related APIs are now app specific - Updated GET
/appid-<appId>/<tenantId>/recipe/session/user
- Adds
fetchAcrossAllTenants
with defaulttrue
- controls fetching of sessions across all tenants or only a particular tenant
- Adds
- Updated POST
/appid-<appId>/<tenantId>/recipe/session/remove
- Adds
revokeAcrossAllTenants
with defaulttrue
- controls revoking of sessions across all tenants or only a particular tenant
- Adds
- Updated telemetry to send
connectionUriDomain
,appId
andmau
information - Updated feature flag stats to report
usersCount
per tenant
- Support for multitenancy.
- New config
supertokens_saas_secret
added to support multitenancy in SaaS mode. - New config
supertokens_default_cdi_version
is added to specify the version of CDI core must assume when the version is not specified in the request. If this config is not specified, the core will assume the latest version.
- Fixes an issue where session verification would fail for JWTs created using the JWT recipe
- Modifies the
/recipe/dashboard/session/verify
API to include the user's email in the response - Support for multitenancy
- New APIs to manage apps and tenants
/recipe/multitenancy/connectionuridomain
PUT/recipe/multitenancy/connectionuridomain/remove
POST/recipe/multitenancy/connectionuridomain/list
GET/recipe/multitenancy/app
PUT/recipe/multitenancy/app/remove
POST/recipe/multitenancy/app/list
GET/appid-<appid>/recipe/multitenancy/tenant
PUT/appid-<appid>/<tenantid>/recipe/multitenancy/tenant
GET/appid-<appid>/recipe/multitenancy/tenant/remove
POST/appid-<appid>/recipe/multitenancy/tenant/list
GET/appid-<appid>/recipe/multitenancy/config/thirdparty
PUT/appid-<appid>/recipe/multitenancy/config/thirdparty/remove
POST/appid-<appid>/<tenantid>/recipe/multitenancy/tenant/user
POST/appid-<appid>/<tenantid>/recipe/multitenancy/tenant/user/remove
POST
- API paths can be prefixed with
/appid-<appid>/<tenantid>
to perform app or tenant specific operations.
- New APIs to manage apps and tenants
-
Ensure that the core is already upgraded to version 5.0.0 (CDI version 2.21)
-
Stop the core instance(s)
-
Run the migration script
If using PostgreSQL
-- General Tables CREATE TABLE IF NOT EXISTS apps ( app_id VARCHAR(64) NOT NULL DEFAULT 'public', created_at_time BIGINT, CONSTRAINT apps_pkey PRIMARY KEY(app_id) ); INSERT INTO apps (app_id, created_at_time) VALUES ('public', 0) ON CONFLICT DO NOTHING; ------------------------------------------------------------ CREATE TABLE IF NOT EXISTS tenants ( app_id VARCHAR(64) NOT NULL DEFAULT 'public', tenant_id VARCHAR(64) NOT NULL DEFAULT 'public', created_at_time BIGINT , CONSTRAINT tenants_pkey PRIMARY KEY (app_id, tenant_id), CONSTRAINT tenants_app_id_fkey FOREIGN KEY(app_id) REFERENCES apps (app_id) ON DELETE CASCADE ); INSERT INTO tenants (app_id, tenant_id, created_at_time) VALUES ('public', 'public', 0) ON CONFLICT DO NOTHING; CREATE INDEX IF NOT EXISTS tenants_app_id_index ON tenants (app_id); ------------------------------------------------------------ ALTER TABLE key_value ADD COLUMN IF NOT EXISTS app_id VARCHAR(64) DEFAULT 'public', ADD COLUMN IF NOT EXISTS tenant_id VARCHAR(64) DEFAULT 'public'; ALTER TABLE key_value DROP CONSTRAINT key_value_pkey; ALTER TABLE key_value ADD CONSTRAINT key_value_pkey PRIMARY KEY (app_id, tenant_id, name); ALTER TABLE key_value DROP CONSTRAINT IF EXISTS key_value_tenant_id_fkey; ALTER TABLE key_value ADD CONSTRAINT key_value_tenant_id_fkey FOREIGN KEY (app_id, tenant_id) REFERENCES tenants (app_id, tenant_id) ON DELETE CASCADE; CREATE INDEX IF NOT EXISTS key_value_tenant_id_index ON key_value (app_id, tenant_id); ------------------------------------------------------------ CREATE TABLE IF NOT EXISTS app_id_to_user_id ( app_id VARCHAR(64) NOT NULL DEFAULT 'public', user_id CHAR(36) NOT NULL, recipe_id VARCHAR(128) NOT NULL, CONSTRAINT app_id_to_user_id_pkey PRIMARY KEY (app_id, user_id), CONSTRAINT app_id_to_user_id_app_id_fkey FOREIGN KEY(app_id) REFERENCES apps (app_id) ON DELETE CASCADE ); INSERT INTO app_id_to_user_id (user_id, recipe_id) SELECT user_id, recipe_id FROM all_auth_recipe_users ON CONFLICT DO NOTHING; CREATE INDEX IF NOT EXISTS app_id_to_user_id_app_id_index ON app_id_to_user_id (app_id); ------------------------------------------------------------ ALTER TABLE all_auth_recipe_users ADD COLUMN IF NOT EXISTS app_id VARCHAR(64) DEFAULT 'public', ADD COLUMN IF NOT EXISTS tenant_id VARCHAR(64) DEFAULT 'public'; ALTER TABLE all_auth_recipe_users DROP CONSTRAINT all_auth_recipe_users_pkey CASCADE; ALTER TABLE all_auth_recipe_users ADD CONSTRAINT all_auth_recipe_users_pkey PRIMARY KEY (app_id, tenant_id, user_id); ALTER TABLE all_auth_recipe_users DROP CONSTRAINT IF EXISTS all_auth_recipe_users_tenant_id_fkey; ALTER TABLE all_auth_recipe_users ADD CONSTRAINT all_auth_recipe_users_tenant_id_fkey FOREIGN KEY (app_id, tenant_id) REFERENCES tenants (app_id, tenant_id) ON DELETE CASCADE; ALTER TABLE all_auth_recipe_users DROP CONSTRAINT IF EXISTS all_auth_recipe_users_user_id_fkey; ALTER TABLE all_auth_recipe_users ADD CONSTRAINT all_auth_recipe_users_user_id_fkey FOREIGN KEY (app_id, user_id) REFERENCES app_id_to_user_id (app_id, user_id) ON DELETE CASCADE; DROP INDEX all_auth_recipe_users_pagination_index; CREATE INDEX all_auth_recipe_users_pagination_index ON all_auth_recipe_users (time_joined DESC, user_id DESC, tenant_id DESC, app_id DESC); CREATE INDEX IF NOT EXISTS all_auth_recipe_user_id_index ON all_auth_recipe_users (app_id, user_id); CREATE INDEX IF NOT EXISTS all_auth_recipe_tenant_id_index ON all_auth_recipe_users (app_id, tenant_id); -- Multitenancy CREATE TABLE IF NOT EXISTS tenant_configs ( connection_uri_domain VARCHAR(256) DEFAULT '', app_id VARCHAR(64) DEFAULT 'public', tenant_id VARCHAR(64) DEFAULT 'public', core_config TEXT, email_password_enabled BOOLEAN, passwordless_enabled BOOLEAN, third_party_enabled BOOLEAN, CONSTRAINT tenant_configs_pkey PRIMARY KEY (connection_uri_domain, app_id, tenant_id) ); ------------------------------------------------------------ CREATE TABLE IF NOT EXISTS tenant_thirdparty_providers ( connection_uri_domain VARCHAR(256) DEFAULT '', app_id VARCHAR(64) DEFAULT 'public', tenant_id VARCHAR(64) DEFAULT 'public', third_party_id VARCHAR(28) NOT NULL, name VARCHAR(64), authorization_endpoint TEXT, authorization_endpoint_query_params TEXT, token_endpoint TEXT, token_endpoint_body_params TEXT, user_info_endpoint TEXT, user_info_endpoint_query_params TEXT, user_info_endpoint_headers TEXT, jwks_uri TEXT, oidc_discovery_endpoint TEXT, require_email BOOLEAN, user_info_map_from_id_token_payload_user_id VARCHAR(64), user_info_map_from_id_token_payload_email VARCHAR(64), user_info_map_from_id_token_payload_email_verified VARCHAR(64), user_info_map_from_user_info_endpoint_user_id VARCHAR(64), user_info_map_from_user_info_endpoint_email VARCHAR(64), user_info_map_from_user_info_endpoint_email_verified VARCHAR(64), CONSTRAINT tenant_thirdparty_providers_pkey PRIMARY KEY (connection_uri_domain, app_id, tenant_id, third_party_id), CONSTRAINT tenant_thirdparty_providers_tenant_id_fkey FOREIGN KEY(connection_uri_domain, app_id, tenant_id) REFERENCES tenant_configs (connection_uri_domain, app_id, tenant_id) ON DELETE CASCADE ); CREATE INDEX IF NOT EXISTS tenant_thirdparty_providers_tenant_id_index ON tenant_thirdparty_providers (connection_uri_domain, app_id, tenant_id); ------------------------------------------------------------ CREATE TABLE IF NOT EXISTS tenant_thirdparty_provider_clients ( connection_uri_domain VARCHAR(256) DEFAULT '', app_id VARCHAR(64) DEFAULT 'public', tenant_id VARCHAR(64) DEFAULT 'public', third_party_id VARCHAR(28) NOT NULL, client_type VARCHAR(64) NOT NULL DEFAULT '', client_id VARCHAR(256) NOT NULL, client_secret TEXT, scope VARCHAR(128)[], force_pkce BOOLEAN, additional_config TEXT, CONSTRAINT tenant_thirdparty_provider_clients_pkey PRIMARY KEY (connection_uri_domain, app_id, tenant_id, third_party_id, client_type), CONSTRAINT tenant_thirdparty_provider_clients_third_party_id_fkey FOREIGN KEY (connection_uri_domain, app_id, tenant_id, third_party_id) REFERENCES tenant_thirdparty_providers (connection_uri_domain, app_id, tenant_id, third_party_id) ON DELETE CASCADE ); CREATE INDEX IF NOT EXISTS tenant_thirdparty_provider_clients_third_party_id_index ON tenant_thirdparty_provider_clients (connection_uri_domain, app_id, tenant_id, third_party_id); -- Session ALTER TABLE session_info ADD COLUMN IF NOT EXISTS app_id VARCHAR(64) DEFAULT 'public', ADD COLUMN IF NOT EXISTS tenant_id VARCHAR(64) DEFAULT 'public'; ALTER TABLE session_info DROP CONSTRAINT session_info_pkey CASCADE; ALTER TABLE session_info ADD CONSTRAINT session_info_pkey PRIMARY KEY (app_id, tenant_id, session_handle); ALTER TABLE session_info DROP CONSTRAINT IF EXISTS session_info_tenant_id_fkey; ALTER TABLE session_info ADD CONSTRAINT session_info_tenant_id_fkey FOREIGN KEY (app_id, tenant_id) REFERENCES tenants (app_id, tenant_id) ON DELETE CASCADE; CREATE INDEX IF NOT EXISTS session_expiry_index ON session_info (expires_at); CREATE INDEX IF NOT EXISTS session_info_tenant_id_index ON session_info (app_id, tenant_id); ------------------------------------------------------------ ALTER TABLE session_access_token_signing_keys ADD COLUMN IF NOT EXISTS app_id VARCHAR(64) DEFAULT 'public'; ALTER TABLE session_access_token_signing_keys DROP CONSTRAINT session_access_token_signing_keys_pkey CASCADE; ALTER TABLE session_access_token_signing_keys ADD CONSTRAINT session_access_token_signing_keys_pkey PRIMARY KEY (app_id, created_at_time); ALTER TABLE session_access_token_signing_keys DROP CONSTRAINT IF EXISTS session_access_token_signing_keys_app_id_fkey; ALTER TABLE session_access_token_signing_keys ADD CONSTRAINT session_access_token_signing_keys_app_id_fkey FOREIGN KEY (app_id) REFERENCES apps (app_id) ON DELETE CASCADE; CREATE INDEX IF NOT EXISTS access_token_signing_keys_app_id_index ON session_access_token_signing_keys (app_id); -- JWT ALTER TABLE jwt_signing_keys ADD COLUMN IF NOT EXISTS app_id VARCHAR(64) DEFAULT 'public'; ALTER TABLE jwt_signing_keys DROP CONSTRAINT jwt_signing_keys_pkey CASCADE; ALTER TABLE jwt_signing_keys ADD CONSTRAINT jwt_signing_keys_pkey PRIMARY KEY (app_id, key_id); ALTER TABLE jwt_signing_keys DROP CONSTRAINT IF EXISTS jwt_signing_keys_app_id_fkey; ALTER TABLE jwt_signing_keys ADD CONSTRAINT jwt_signing_keys_app_id_fkey FOREIGN KEY (app_id) REFERENCES apps (app_id) ON DELETE CASCADE; CREATE INDEX IF NOT EXISTS jwt_signing_keys_app_id_index ON jwt_signing_keys (app_id); -- EmailVerification ALTER TABLE emailverification_verified_emails ADD COLUMN IF NOT EXISTS app_id VARCHAR(64) DEFAULT 'public'; ALTER TABLE emailverification_verified_emails DROP CONSTRAINT emailverification_verified_emails_pkey CASCADE; ALTER TABLE emailverification_verified_emails ADD CONSTRAINT emailverification_verified_emails_pkey PRIMARY KEY (app_id, user_id, email); ALTER TABLE emailverification_verified_emails DROP CONSTRAINT IF EXISTS emailverification_verified_emails_app_id_fkey; ALTER TABLE emailverification_verified_emails ADD CONSTRAINT emailverification_verified_emails_app_id_fkey FOREIGN KEY (app_id) REFERENCES apps (app_id) ON DELETE CASCADE; CREATE INDEX IF NOT EXISTS emailverification_verified_emails_app_id_index ON emailverification_verified_emails (app_id); ------------------------------------------------------------ ALTER TABLE emailverification_tokens ADD COLUMN IF NOT EXISTS app_id VARCHAR(64) DEFAULT 'public', ADD COLUMN IF NOT EXISTS tenant_id VARCHAR(64) DEFAULT 'public'; ALTER TABLE emailverification_tokens DROP CONSTRAINT emailverification_tokens_pkey CASCADE; ALTER TABLE emailverification_tokens ADD CONSTRAINT emailverification_tokens_pkey PRIMARY KEY (app_id, tenant_id, user_id, email, token); ALTER TABLE emailverification_tokens DROP CONSTRAINT IF EXISTS emailverification_tokens_tenant_id_fkey; ALTER TABLE emailverification_tokens ADD CONSTRAINT emailverification_tokens_tenant_id_fkey FOREIGN KEY (app_id, tenant_id) REFERENCES tenants (app_id, tenant_id) ON DELETE CASCADE; CREATE INDEX IF NOT EXISTS emailverification_tokens_tenant_id_index ON emailverification_tokens (app_id, tenant_id); -- EmailPassword ALTER TABLE emailpassword_users ADD COLUMN IF NOT EXISTS app_id VARCHAR(64) DEFAULT 'public'; ALTER TABLE emailpassword_users DROP CONSTRAINT emailpassword_users_pkey CASCADE; ALTER TABLE emailpassword_users DROP CONSTRAINT IF EXISTS emailpassword_users_email_key CASCADE; ALTER TABLE emailpassword_users ADD CONSTRAINT emailpassword_users_pkey PRIMARY KEY (app_id, user_id); ALTER TABLE emailpassword_users DROP CONSTRAINT IF EXISTS emailpassword_users_user_id_fkey; ALTER TABLE emailpassword_users ADD CONSTRAINT emailpassword_users_user_id_fkey FOREIGN KEY (app_id, user_id) REFERENCES app_id_to_user_id (app_id, user_id) ON DELETE CASCADE; ------------------------------------------------------------ CREATE TABLE IF NOT EXISTS emailpassword_user_to_tenant ( app_id VARCHAR(64) DEFAULT 'public', tenant_id VARCHAR(64) DEFAULT 'public', user_id CHAR(36) NOT NULL, email VARCHAR(256) NOT NULL, CONSTRAINT emailpassword_user_to_tenant_email_key UNIQUE (app_id, tenant_id, email), CONSTRAINT emailpassword_user_to_tenant_pkey PRIMARY KEY (app_id, tenant_id, user_id), CONSTRAINT emailpassword_user_to_tenant_user_id_fkey FOREIGN KEY (app_id, tenant_id, user_id) REFERENCES all_auth_recipe_users (app_id, tenant_id, user_id) ON DELETE CASCADE ); ALTER TABLE emailpassword_user_to_tenant DROP CONSTRAINT IF EXISTS emailpassword_user_to_tenant_email_key; ALTER TABLE emailpassword_user_to_tenant ADD CONSTRAINT emailpassword_user_to_tenant_email_key UNIQUE (app_id, tenant_id, email); ALTER TABLE emailpassword_user_to_tenant DROP CONSTRAINT IF EXISTS emailpassword_user_to_tenant_user_id_fkey; ALTER TABLE emailpassword_user_to_tenant ADD CONSTRAINT emailpassword_user_to_tenant_user_id_fkey FOREIGN KEY (app_id, tenant_id, user_id) REFERENCES all_auth_recipe_users (app_id, tenant_id, user_id) ON DELETE CASCADE; INSERT INTO emailpassword_user_to_tenant (user_id, email) SELECT user_id, email FROM emailpassword_users ON CONFLICT DO NOTHING; ------------------------------------------------------------ ALTER TABLE emailpassword_pswd_reset_tokens ADD COLUMN IF NOT EXISTS app_id VARCHAR(64) DEFAULT 'public'; ALTER TABLE emailpassword_pswd_reset_tokens DROP CONSTRAINT emailpassword_pswd_reset_tokens_pkey CASCADE; ALTER TABLE emailpassword_pswd_reset_tokens ADD CONSTRAINT emailpassword_pswd_reset_tokens_pkey PRIMARY KEY (app_id, user_id, token); ALTER TABLE emailpassword_pswd_reset_tokens DROP CONSTRAINT IF EXISTS emailpassword_pswd_reset_tokens_user_id_fkey; ALTER TABLE emailpassword_pswd_reset_tokens ADD CONSTRAINT emailpassword_pswd_reset_tokens_user_id_fkey FOREIGN KEY (app_id, user_id) REFERENCES emailpassword_users (app_id, user_id) ON DELETE CASCADE; CREATE INDEX IF NOT EXISTS emailpassword_pswd_reset_tokens_user_id_index ON emailpassword_pswd_reset_tokens (app_id, user_id); -- Passwordless ALTER TABLE passwordless_users ADD COLUMN IF NOT EXISTS app_id VARCHAR(64) DEFAULT 'public'; ALTER TABLE passwordless_users DROP CONSTRAINT passwordless_users_pkey CASCADE; ALTER TABLE passwordless_users ADD CONSTRAINT passwordless_users_pkey PRIMARY KEY (app_id, user_id); ALTER TABLE passwordless_users DROP CONSTRAINT IF EXISTS passwordless_users_email_key; ALTER TABLE passwordless_users DROP CONSTRAINT IF EXISTS passwordless_users_phone_number_key; ALTER TABLE passwordless_users DROP CONSTRAINT IF EXISTS passwordless_users_user_id_fkey; ALTER TABLE passwordless_users ADD CONSTRAINT passwordless_users_user_id_fkey FOREIGN KEY (app_id, user_id) REFERENCES app_id_to_user_id (app_id, user_id) ON DELETE CASCADE; ------------------------------------------------------------ CREATE TABLE IF NOT EXISTS passwordless_user_to_tenant ( app_id VARCHAR(64) DEFAULT 'public', tenant_id VARCHAR(64) DEFAULT 'public', user_id CHAR(36) NOT NULL, email VARCHAR(256), phone_number VARCHAR(256), CONSTRAINT passwordless_user_to_tenant_email_key UNIQUE (app_id, tenant_id, email), CONSTRAINT passwordless_user_to_tenant_phone_number_key UNIQUE (app_id, tenant_id, phone_number), CONSTRAINT passwordless_user_to_tenant_pkey PRIMARY KEY (app_id, tenant_id, user_id), CONSTRAINT passwordless_user_to_tenant_user_id_fkey FOREIGN KEY (app_id, tenant_id, user_id) REFERENCES all_auth_recipe_users (app_id, tenant_id, user_id) ON DELETE CASCADE ); ALTER TABLE passwordless_user_to_tenant DROP CONSTRAINT IF EXISTS passwordless_user_to_tenant_user_id_fkey; ALTER TABLE passwordless_user_to_tenant ADD CONSTRAINT passwordless_user_to_tenant_user_id_fkey FOREIGN KEY (app_id, tenant_id, user_id) REFERENCES all_auth_recipe_users (app_id, tenant_id, user_id) ON DELETE CASCADE; INSERT INTO passwordless_user_to_tenant (user_id, email, phone_number) SELECT user_id, email, phone_number FROM passwordless_users ON CONFLICT DO NOTHING; ------------------------------------------------------------ ALTER TABLE passwordless_devices ADD COLUMN IF NOT EXISTS app_id VARCHAR(64) DEFAULT 'public', ADD COLUMN IF NOT EXISTS tenant_id VARCHAR(64) DEFAULT 'public'; ALTER TABLE passwordless_devices DROP CONSTRAINT passwordless_devices_pkey CASCADE; ALTER TABLE passwordless_devices ADD CONSTRAINT passwordless_devices_pkey PRIMARY KEY (app_id, tenant_id, device_id_hash); ALTER TABLE passwordless_devices DROP CONSTRAINT IF EXISTS passwordless_devices_tenant_id_fkey; ALTER TABLE passwordless_devices ADD CONSTRAINT passwordless_devices_tenant_id_fkey FOREIGN KEY (app_id, tenant_id) REFERENCES tenants (app_id, tenant_id) ON DELETE CASCADE; DROP INDEX IF EXISTS passwordless_devices_email_index; CREATE INDEX IF NOT EXISTS passwordless_devices_email_index ON passwordless_devices (app_id, tenant_id, email); DROP INDEX IF EXISTS passwordless_devices_phone_number_index; CREATE INDEX IF NOT EXISTS passwordless_devices_phone_number_index ON passwordless_devices (app_id, tenant_id, phone_number); CREATE INDEX IF NOT EXISTS passwordless_devices_tenant_id_index ON passwordless_devices (app_id, tenant_id); ------------------------------------------------------------ ALTER TABLE passwordless_codes ADD COLUMN IF NOT EXISTS app_id VARCHAR(64) DEFAULT 'public', ADD COLUMN IF NOT EXISTS tenant_id VARCHAR(64) DEFAULT 'public'; ALTER TABLE passwordless_codes DROP CONSTRAINT passwordless_codes_pkey CASCADE; ALTER TABLE passwordless_codes ADD CONSTRAINT passwordless_codes_pkey PRIMARY KEY (app_id, tenant_id, code_id); ALTER TABLE passwordless_codes DROP CONSTRAINT IF EXISTS passwordless_codes_device_id_hash_fkey; ALTER TABLE passwordless_codes ADD CONSTRAINT passwordless_codes_device_id_hash_fkey FOREIGN KEY (app_id, tenant_id, device_id_hash) REFERENCES passwordless_devices (app_id, tenant_id, device_id_hash) ON DELETE CASCADE; ALTER TABLE passwordless_codes DROP CONSTRAINT passwordless_codes_link_code_hash_key; ALTER TABLE passwordless_codes DROP CONSTRAINT IF EXISTS passwordless_codes_link_code_hash_key; ALTER TABLE passwordless_codes ADD CONSTRAINT passwordless_codes_link_code_hash_key UNIQUE (app_id, tenant_id, link_code_hash); DROP INDEX IF EXISTS passwordless_codes_created_at_index; CREATE INDEX IF NOT EXISTS passwordless_codes_created_at_index ON passwordless_codes (app_id, tenant_id, created_at); DROP INDEX IF EXISTS passwordless_codes_device_id_hash_index; CREATE INDEX IF NOT EXISTS passwordless_codes_device_id_hash_index ON passwordless_codes (app_id, tenant_id, device_id_hash); -- ThirdParty ALTER TABLE thirdparty_users ADD COLUMN IF NOT EXISTS app_id VARCHAR(64) DEFAULT 'public'; ALTER TABLE thirdparty_users DROP CONSTRAINT thirdparty_users_pkey CASCADE; ALTER TABLE thirdparty_users DROP CONSTRAINT IF EXISTS thirdparty_users_user_id_key CASCADE; ALTER TABLE thirdparty_users ADD CONSTRAINT thirdparty_users_pkey PRIMARY KEY (app_id, user_id); ALTER TABLE thirdparty_users DROP CONSTRAINT IF EXISTS thirdparty_users_user_id_fkey; ALTER TABLE thirdparty_users ADD CONSTRAINT thirdparty_users_user_id_fkey FOREIGN KEY (app_id, user_id) REFERENCES app_id_to_user_id (app_id, user_id) ON DELETE CASCADE; DROP INDEX IF EXISTS thirdparty_users_thirdparty_user_id_index; CREATE INDEX IF NOT EXISTS thirdparty_users_thirdparty_user_id_index ON thirdparty_users (app_id, third_party_id, third_party_user_id); DROP INDEX IF EXISTS thirdparty_users_email_index; CREATE INDEX IF NOT EXISTS thirdparty_users_email_index ON thirdparty_users (app_id, email); ------------------------------------------------------------ CREATE TABLE IF NOT EXISTS thirdparty_user_to_tenant ( app_id VARCHAR(64) DEFAULT 'public', tenant_id VARCHAR(64) DEFAULT 'public', user_id CHAR(36) NOT NULL, third_party_id VARCHAR(28) NOT NULL, third_party_user_id VARCHAR(256) NOT NULL, CONSTRAINT thirdparty_user_to_tenant_third_party_user_id_key UNIQUE (app_id, tenant_id, third_party_id, third_party_user_id), CONSTRAINT thirdparty_user_to_tenant_pkey PRIMARY KEY (app_id, tenant_id, user_id), CONSTRAINT thirdparty_user_to_tenant_user_id_fkey FOREIGN KEY (app_id, tenant_id, user_id) REFERENCES all_auth_recipe_users (app_id, tenant_id, user_id) ON DELETE CASCADE ); ALTER TABLE thirdparty_user_to_tenant DROP CONSTRAINT IF EXISTS thirdparty_user_to_tenant_third_party_user_id_key; ALTER TABLE thirdparty_user_to_tenant ADD CONSTRAINT thirdparty_user_to_tenant_third_party_user_id_key UNIQUE (app_id, tenant_id, third_party_id, third_party_user_id); ALTER TABLE thirdparty_user_to_tenant DROP CONSTRAINT IF EXISTS thirdparty_user_to_tenant_user_id_fkey; ALTER TABLE thirdparty_user_to_tenant ADD CONSTRAINT thirdparty_user_to_tenant_user_id_fkey FOREIGN KEY (app_id, tenant_id, user_id) REFERENCES all_auth_recipe_users (app_id, tenant_id, user_id) ON DELETE CASCADE; INSERT INTO thirdparty_user_to_tenant (user_id, third_party_id, third_party_user_id) SELECT user_id, third_party_id, third_party_user_id FROM thirdparty_users ON CONFLICT DO NOTHING; -- UserIdMapping ALTER TABLE userid_mapping ADD COLUMN IF NOT EXISTS app_id VARCHAR(64) DEFAULT 'public'; ALTER TABLE userid_mapping DROP CONSTRAINT IF EXISTS userid_mapping_pkey CASCADE; ALTER TABLE userid_mapping ADD CONSTRAINT userid_mapping_pkey PRIMARY KEY (app_id, supertokens_user_id, external_user_id); ALTER TABLE userid_mapping DROP CONSTRAINT IF EXISTS userid_mapping_supertokens_user_id_key; ALTER TABLE userid_mapping ADD CONSTRAINT userid_mapping_supertokens_user_id_key UNIQUE (app_id, supertokens_user_id); ALTER TABLE userid_mapping DROP CONSTRAINT IF EXISTS userid_mapping_external_user_id_key; ALTER TABLE userid_mapping ADD CONSTRAINT userid_mapping_external_user_id_key UNIQUE (app_id, external_user_id); ALTER TABLE userid_mapping DROP CONSTRAINT IF EXISTS userid_mapping_supertokens_user_id_fkey; ALTER TABLE userid_mapping ADD CONSTRAINT userid_mapping_supertokens_user_id_fkey FOREIGN KEY (app_id, supertokens_user_id) REFERENCES app_id_to_user_id (app_id, user_id) ON DELETE CASCADE; CREATE INDEX IF NOT EXISTS userid_mapping_supertokens_user_id_index ON userid_mapping (app_id, supertokens_user_id); -- UserRoles ALTER TABLE roles ADD COLUMN IF NOT EXISTS app_id VARCHAR(64) DEFAULT 'public'; ALTER TABLE roles DROP CONSTRAINT roles_pkey CASCADE; ALTER TABLE roles ADD CONSTRAINT roles_pkey PRIMARY KEY (app_id, role); ALTER TABLE roles DROP CONSTRAINT IF EXISTS roles_app_id_fkey; ALTER TABLE roles ADD CONSTRAINT roles_app_id_fkey FOREIGN KEY (app_id) REFERENCES apps (app_id) ON DELETE CASCADE; CREATE INDEX IF NOT EXISTS roles_app_id_index ON roles (app_id); ------------------------------------------------------------ ALTER TABLE role_permissions ADD COLUMN IF NOT EXISTS app_id VARCHAR(64) DEFAULT 'public'; ALTER TABLE role_permissions DROP CONSTRAINT role_permissions_pkey CASCADE; ALTER TABLE role_permissions ADD CONSTRAINT role_permissions_pkey PRIMARY KEY (app_id, role, permission); ALTER TABLE role_permissions DROP CONSTRAINT IF EXISTS role_permissions_role_fkey; ALTER TABLE role_permissions ADD CONSTRAINT role_permissions_role_fkey FOREIGN KEY (app_id, role) REFERENCES roles (app_id, role) ON DELETE CASCADE; DROP INDEX IF EXISTS role_permissions_permission_index; CREATE INDEX IF NOT EXISTS role_permissions_permission_index ON role_permissions (app_id, permission); CREATE INDEX IF NOT EXISTS role_permissions_role_index ON role_permissions (app_id, role); ------------------------------------------------------------ ALTER TABLE user_roles ADD COLUMN IF NOT EXISTS app_id VARCHAR(64) DEFAULT 'public', ADD COLUMN IF NOT EXISTS tenant_id VARCHAR(64) DEFAULT 'public'; ALTER TABLE user_roles DROP CONSTRAINT user_roles_pkey CASCADE; ALTER TABLE user_roles ADD CONSTRAINT user_roles_pkey PRIMARY KEY (app_id, tenant_id, user_id, role); ALTER TABLE user_roles DROP CONSTRAINT IF EXISTS user_roles_tenant_id_fkey; ALTER TABLE user_roles ADD CONSTRAINT user_roles_tenant_id_fkey FOREIGN KEY (app_id, tenant_id) REFERENCES tenants (app_id, tenant_id) ON DELETE CASCADE; ALTER TABLE user_roles DROP CONSTRAINT IF EXISTS user_roles_role_fkey; ALTER TABLE user_roles ADD CONSTRAINT user_roles_role_fkey FOREIGN KEY (app_id, role) REFERENCES roles (app_id, role) ON DELETE CASCADE; DROP INDEX IF EXISTS user_roles_role_index; CREATE INDEX IF NOT EXISTS user_roles_role_index ON user_roles (app_id, tenant_id, role); CREATE INDEX IF NOT EXISTS user_roles_tenant_id_index ON user_roles (app_id, tenant_id); CREATE INDEX IF NOT EXISTS user_roles_app_id_role_index ON user_roles (app_id, role); -- UserMetadata ALTER TABLE user_metadata ADD COLUMN IF NOT EXISTS app_id VARCHAR(64) DEFAULT 'public'; ALTER TABLE user_metadata DROP CONSTRAINT user_metadata_pkey CASCADE; ALTER TABLE user_metadata ADD CONSTRAINT user_metadata_pkey PRIMARY KEY (app_id, user_id); ALTER TABLE user_metadata DROP CONSTRAINT IF EXISTS user_metadata_app_id_fkey; ALTER TABLE user_metadata ADD CONSTRAINT user_metadata_app_id_fkey FOREIGN KEY (app_id) REFERENCES apps (app_id) ON DELETE CASCADE; CREATE INDEX IF NOT EXISTS user_metadata_app_id_index ON user_metadata (app_id); -- Dashboard ALTER TABLE dashboard_users ADD COLUMN IF NOT EXISTS app_id VARCHAR(64) DEFAULT 'public'; ALTER TABLE dashboard_users DROP CONSTRAINT dashboard_users_pkey CASCADE; ALTER TABLE dashboard_users ADD CONSTRAINT dashboard_users_pkey PRIMARY KEY (app_id, user_id); ALTER TABLE dashboard_users DROP CONSTRAINT IF EXISTS dashboard_users_email_key; ALTER TABLE dashboard_users ADD CONSTRAINT dashboard_users_email_key UNIQUE (app_id, email); ALTER TABLE dashboard_users DROP CONSTRAINT IF EXISTS dashboard_users_app_id_fkey; ALTER TABLE dashboard_users ADD CONSTRAINT dashboard_users_app_id_fkey FOREIGN KEY (app_id) REFERENCES apps (app_id) ON DELETE CASCADE; CREATE INDEX IF NOT EXISTS dashboard_users_app_id_index ON dashboard_users (app_id); ------------------------------------------------------------ ALTER TABLE dashboard_user_sessions ADD COLUMN IF NOT EXISTS app_id VARCHAR(64) DEFAULT 'public'; ALTER TABLE dashboard_user_sessions DROP CONSTRAINT dashboard_user_sessions_pkey CASCADE; ALTER TABLE dashboard_user_sessions ADD CONSTRAINT dashboard_user_sessions_pkey PRIMARY KEY (app_id, session_id); ALTER TABLE dashboard_user_sessions DROP CONSTRAINT IF EXISTS dashboard_user_sessions_user_id_fkey; ALTER TABLE dashboard_user_sessions ADD CONSTRAINT dashboard_user_sessions_user_id_fkey FOREIGN KEY (app_id, user_id) REFERENCES dashboard_users (app_id, user_id) ON DELETE CASCADE; CREATE INDEX IF NOT EXISTS dashboard_user_sessions_user_id_index ON dashboard_user_sessions (app_id, user_id); -- TOTP ALTER TABLE totp_users ADD COLUMN IF NOT EXISTS app_id VARCHAR(64) DEFAULT 'public'; ALTER TABLE totp_users DROP CONSTRAINT totp_users_pkey CASCADE; ALTER TABLE totp_users ADD CONSTRAINT totp_users_pkey PRIMARY KEY (app_id, user_id); ALTER TABLE totp_users DROP CONSTRAINT IF EXISTS totp_users_app_id_fkey; ALTER TABLE totp_users ADD CONSTRAINT totp_users_app_id_fkey FOREIGN KEY (app_id) REFERENCES apps (app_id) ON DELETE CASCADE; CREATE INDEX IF NOT EXISTS totp_users_app_id_index ON totp_users (app_id); ------------------------------------------------------------ ALTER TABLE totp_user_devices ADD COLUMN IF NOT EXISTS app_id VARCHAR(64) DEFAULT 'public'; ALTER TABLE totp_user_devices DROP CONSTRAINT totp_user_devices_pkey; ALTER TABLE totp_user_devices ADD CONSTRAINT totp_user_devices_pkey PRIMARY KEY (app_id, user_id, device_name); ALTER TABLE totp_user_devices DROP CONSTRAINT IF EXISTS totp_user_devices_user_id_fkey; ALTER TABLE totp_user_devices ADD CONSTRAINT totp_user_devices_user_id_fkey FOREIGN KEY (app_id, user_id) REFERENCES totp_users (app_id, user_id) ON DELETE CASCADE; CREATE INDEX IF NOT EXISTS totp_user_devices_user_id_index ON totp_user_devices (app_id, user_id); ------------------------------------------------------------ ALTER TABLE totp_used_codes ADD COLUMN IF NOT EXISTS app_id VARCHAR(64) DEFAULT 'public', ADD COLUMN IF NOT EXISTS tenant_id VARCHAR(64) DEFAULT 'public'; ALTER TABLE totp_used_codes DROP CONSTRAINT totp_used_codes_pkey CASCADE; ALTER TABLE totp_used_codes ADD CONSTRAINT totp_used_codes_pkey PRIMARY KEY (app_id, tenant_id, user_id, created_time_ms); ALTER TABLE totp_used_codes DROP CONSTRAINT IF EXISTS totp_used_codes_user_id_fkey; ALTER TABLE totp_used_codes ADD CONSTRAINT totp_used_codes_user_id_fkey FOREIGN KEY (app_id, user_id) REFERENCES totp_users (app_id, user_id) ON DELETE CASCADE; ALTER TABLE totp_used_codes DROP CONSTRAINT IF EXISTS totp_used_codes_tenant_id_fkey; ALTER TABLE totp_used_codes ADD CONSTRAINT totp_used_codes_tenant_id_fkey FOREIGN KEY (app_id, tenant_id) REFERENCES tenants (app_id, tenant_id) ON DELETE CASCADE; DROP INDEX IF EXISTS totp_used_codes_expiry_time_ms_index; CREATE INDEX IF NOT EXISTS totp_used_codes_expiry_time_ms_index ON totp_used_codes (app_id, tenant_id, expiry_time_ms); CREATE INDEX IF NOT EXISTS totp_used_codes_user_id_index ON totp_used_codes (app_id, user_id); CREATE INDEX IF NOT EXISTS totp_used_codes_tenant_id_index ON totp_used_codes (app_id, tenant_id); -- ActiveUsers ALTER TABLE user_last_active ADD COLUMN IF NOT EXISTS app_id VARCHAR(64) DEFAULT 'public'; ALTER TABLE user_last_active DROP CONSTRAINT user_last_active_pkey CASCADE; ALTER TABLE user_last_active ADD CONSTRAINT user_last_active_pkey PRIMARY KEY (app_id, user_id); ALTER TABLE user_last_active DROP CONSTRAINT IF EXISTS user_last_active_app_id_fkey; ALTER TABLE user_last_active ADD CONSTRAINT user_last_active_app_id_fkey FOREIGN KEY (app_id) REFERENCES apps (app_id) ON DELETE CASCADE; CREATE INDEX IF NOT EXISTS user_last_active_app_id_index ON user_last_active (app_id);
If using MySQL
-- helper stored procedures DELIMITER // CREATE PROCEDURE st_drop_all_fkeys() BEGIN DECLARE done INT DEFAULT FALSE; DECLARE dropCommand VARCHAR(255); DECLARE dropCur CURSOR for SELECT concat('ALTER TABLE ', table_schema,'.',table_name,' DROP FOREIGN KEY ', constraint_name, ';') FROM information_schema.table_constraints WHERE constraint_type='FOREIGN KEY' AND table_schema = DATABASE() AND table_name in ( 'all_auth_recipe_users', 'dashboard_user_sessions', 'dashboard_users', 'emailpassword_pswd_reset_tokens', 'emailpassword_users', 'emailverification_tokens', 'emailverification_verified_emails', 'jwt_signing_keys', 'key_value', 'passwordless_codes', 'passwordless_devices', 'passwordless_users', 'role_permissions', 'roles', 'session_access_token_signing_keys', 'session_info', 'thirdparty_users', 'totp_used_codes', 'totp_user_devices', 'totp_users', 'user_last_active', 'user_metadata', 'user_roles', 'userid_mapping' ); DECLARE CONTINUE handler for NOT found SET done = true; OPEN dropCur; read_loop: LOOP FETCH dropCur INTO dropCommand; IF done THEN leave read_loop; END IF; SET @sdropCommand = dropCommand; PREPARE dropClientUpdateKeyStmt FROM @sdropCommand; EXECUTE dropClientUpdateKeyStmt; DEALLOCATE prepare dropClientUpdateKeyStmt; END LOOP; CLOSE dropCur; END // -- CREATE PROCEDURE st_drop_all_pkeys() BEGIN DECLARE done INT DEFAULT FALSE; DECLARE dropCommand VARCHAR(255); DECLARE dropCur CURSOR for SELECT concat('ALTER TABLE ', table_schema,'.',table_name,' DROP PRIMARY KEY ', ';') FROM information_schema.table_constraints WHERE constraint_type='PRIMARY KEY' AND table_schema = DATABASE() AND table_name in ( 'all_auth_recipe_users', 'dashboard_user_sessions', 'dashboard_users', 'emailpassword_pswd_reset_tokens', 'emailpassword_users', 'emailverification_tokens', 'emailverification_verified_emails', 'jwt_signing_keys', 'key_value', 'passwordless_codes', 'passwordless_devices', 'passwordless_users', 'role_permissions', 'roles', 'session_access_token_signing_keys', 'session_info', 'thirdparty_users', 'totp_used_codes', 'totp_user_devices', 'totp_users', 'user_last_active', 'user_metadata', 'user_roles', 'userid_mapping' ); DECLARE CONTINUE handler for NOT found SET done = true; OPEN dropCur; read_loop: LOOP FETCH dropCur INTO dropCommand; IF done THEN leave read_loop; END IF; SET @sdropCommand = dropCommand; PREPARE dropClientUpdateKeyStmt FROM @sdropCommand; EXECUTE dropClientUpdateKeyStmt; DEALLOCATE prepare dropClientUpdateKeyStmt; END LOOP; CLOSE dropCur; END // -- CREATE PROCEDURE st_drop_all_keys() BEGIN DECLARE done INT DEFAULT FALSE; DECLARE dropCommand VARCHAR(255); DECLARE dropCur CURSOR for SELECT concat('ALTER TABLE ', table_schema,'.',table_name,' DROP INDEX ', constraint_name, ';') FROM information_schema.table_constraints WHERE constraint_type='UNIQUE' AND table_schema = DATABASE() AND table_name in ( 'all_auth_recipe_users', 'dashboard_user_sessions', 'dashboard_users', 'emailpassword_pswd_reset_tokens', 'emailpassword_users', 'emailverification_tokens', 'emailverification_verified_emails', 'jwt_signing_keys', 'key_value', 'passwordless_codes', 'passwordless_devices', 'passwordless_users', 'role_permissions', 'roles', 'session_access_token_signing_keys', 'session_info', 'thirdparty_users', 'totp_used_codes', 'totp_user_devices', 'totp_users', 'user_last_active', 'user_metadata', 'user_roles', 'userid_mapping' ); DECLARE CONTINUE handler for NOT found SET done = true; OPEN dropCur; read_loop: LOOP FETCH dropCur INTO dropCommand; IF done THEN leave read_loop; END IF; SET @sdropCommand = dropCommand; PREPARE dropClientUpdateKeyStmt FROM @sdropCommand; EXECUTE dropClientUpdateKeyStmt; DEALLOCATE prepare dropClientUpdateKeyStmt; END LOOP; CLOSE dropCur; END // -- CREATE PROCEDURE st_drop_all_indexes() BEGIN DECLARE done INT DEFAULT FALSE; DECLARE dropCommand VARCHAR(255); DECLARE dropCur CURSOR for SELECT DISTINCT concat('ALTER TABLE ', table_schema, '.', table_name, ' DROP INDEX ', index_name, ';') FROM information_schema.statistics WHERE NON_UNIQUE = 1 AND table_schema = database() AND table_name in ( 'all_auth_recipe_users', 'dashboard_user_sessions', 'dashboard_users', 'emailpassword_pswd_reset_tokens', 'emailpassword_users', 'emailverification_tokens', 'emailverification_verified_emails', 'jwt_signing_keys', 'key_value', 'passwordless_codes', 'passwordless_devices', 'passwordless_users', 'role_permissions', 'roles', 'session_access_token_signing_keys', 'session_info', 'thirdparty_users', 'totp_used_codes', 'totp_user_devices', 'totp_users', 'user_last_active', 'user_metadata', 'user_roles', 'userid_mapping' ); DECLARE CONTINUE handler for NOT found SET done = true; OPEN dropCur; read_loop: LOOP FETCH dropCur INTO dropCommand; IF done THEN leave read_loop; END IF; SET @sdropCommand = dropCommand; PREPARE dropClientUpdateKeyStmt FROM @sdropCommand; EXECUTE dropClientUpdateKeyStmt; DEALLOCATE prepare dropClientUpdateKeyStmt; END LOOP; CLOSE dropCur; END // -- CREATE PROCEDURE st_add_column_if_not_exists( IN p_table_name varchar(50), IN p_column_name varchar(50), IN p_column_type varchar(50), IN p_additional varchar(100), OUT p_status_message varchar(100)) READS SQL DATA BEGIN DECLARE v_count INT; # Check wether column exist or not SELECT count(*) INTO v_count FROM information_schema.columns WHERE table_schema = database() AND table_name = p_table_name AND column_name = p_column_name; IF v_count > 0 THEN # Return column already exists message SELECT 'Column already Exists' INTO p_status_message; ELSE # Add Column and return success message set @ddl_addcolumn=CONCAT('ALTER TABLE ',database(),'.',p_table_name, ' ADD COLUMN ',p_column_name,' ',p_column_type,' ',p_additional); prepare add_column_sql from @ddl_addcolumn; execute add_column_sql; SELECT 'Column Successfully Created!' INTO p_status_message; END IF; END // DELIMITER ; -- Drop constraints and indexes CALL st_drop_all_fkeys(); CALL st_drop_all_keys(); CALL st_drop_all_pkeys(); CALL st_drop_all_indexes(); -- General Tables CREATE TABLE IF NOT EXISTS apps ( app_id VARCHAR(64) NOT NULL DEFAULT 'public', created_at_time BIGINT UNSIGNED ); ALTER TABLE apps ADD PRIMARY KEY(app_id); INSERT IGNORE INTO apps (app_id, created_at_time) VALUES ('public', 0); -- CREATE TABLE IF NOT EXISTS tenants ( app_id VARCHAR(64) NOT NULL DEFAULT 'public', tenant_id VARCHAR(64) NOT NULL DEFAULT 'public', created_at_time BIGINT UNSIGNED ); ALTER TABLE tenants ADD PRIMARY KEY(app_id, tenant_id); ALTER TABLE tenants ADD FOREIGN KEY (app_id) REFERENCES apps (app_id) ON DELETE CASCADE; INSERT IGNORE INTO tenants (app_id, tenant_id, created_at_time) VALUES ('public', 'public', 0); -- CALL st_add_column_if_not_exists('key_value', 'app_id', 'VARCHAR(64)', 'NOT NULL DEFAULT \'public\'', @status_message); CALL st_add_column_if_not_exists('key_value', 'tenant_id', 'VARCHAR(64)', 'NOT NULL DEFAULT \'public\'', @status_message); ALTER TABLE key_value ADD PRIMARY KEY (app_id, tenant_id, name); ALTER TABLE key_value ADD FOREIGN KEY (app_id, tenant_id) REFERENCES tenants (app_id, tenant_id) ON DELETE CASCADE; -- CREATE TABLE IF NOT EXISTS app_id_to_user_id ( app_id VARCHAR(64) NOT NULL DEFAULT 'public', user_id CHAR(36) NOT NULL, recipe_id VARCHAR(128) NOT NULL ); ALTER TABLE app_id_to_user_id ADD PRIMARY KEY (app_id, user_id); ALTER TABLE app_id_to_user_id ADD FOREIGN KEY (app_id) REFERENCES apps (app_id) ON DELETE CASCADE; INSERT IGNORE INTO app_id_to_user_id (user_id, recipe_id) SELECT user_id, recipe_id FROM all_auth_recipe_users; -- CALL st_add_column_if_not_exists('all_auth_recipe_users', 'app_id', 'VARCHAR(64)', 'NOT NULL DEFAULT \'public\'', @status_message); CALL st_add_column_if_not_exists('all_auth_recipe_users', 'tenant_id', 'VARCHAR(64)', 'NOT NULL DEFAULT \'public\'', @status_message); ALTER TABLE all_auth_recipe_users ADD PRIMARY KEY (app_id, tenant_id, user_id); ALTER TABLE all_auth_recipe_users ADD FOREIGN KEY (app_id, tenant_id) REFERENCES tenants (app_id, tenant_id) ON DELETE CASCADE; ALTER TABLE all_auth_recipe_users ADD FOREIGN KEY (app_id, user_id) REFERENCES app_id_to_user_id (app_id, user_id) ON DELETE CASCADE; CREATE INDEX all_auth_recipe_users_pagination_index ON all_auth_recipe_users (time_joined DESC, user_id DESC, tenant_id DESC, app_id DESC); -- Multitenancy CREATE TABLE IF NOT EXISTS tenant_configs ( connection_uri_domain VARCHAR(256) DEFAULT '', app_id VARCHAR(64) DEFAULT 'public', tenant_id VARCHAR(64) DEFAULT 'public', core_config TEXT, email_password_enabled BOOLEAN, passwordless_enabled BOOLEAN, third_party_enabled BOOLEAN ); ALTER TABLE tenant_configs ADD PRIMARY KEY (connection_uri_domain, app_id, tenant_id); -- CREATE TABLE IF NOT EXISTS tenant_thirdparty_providers ( connection_uri_domain VARCHAR(256) DEFAULT '', app_id VARCHAR(64) DEFAULT 'public', tenant_id VARCHAR(64) DEFAULT 'public', third_party_id VARCHAR(28) NOT NULL, name VARCHAR(64), authorization_endpoint TEXT, authorization_endpoint_query_params TEXT, token_endpoint TEXT, token_endpoint_body_params TEXT, user_info_endpoint TEXT, user_info_endpoint_query_params TEXT, user_info_endpoint_headers TEXT, jwks_uri TEXT, oidc_discovery_endpoint TEXT, require_email BOOLEAN, user_info_map_from_id_token_payload_user_id VARCHAR(64), user_info_map_from_id_token_payload_email VARCHAR(64), user_info_map_from_id_token_payload_email_verified VARCHAR(64), user_info_map_from_user_info_endpoint_user_id VARCHAR(64), user_info_map_from_user_info_endpoint_email VARCHAR(64), user_info_map_from_user_info_endpoint_email_verified VARCHAR(64) ); ALTER TABLE tenant_thirdparty_providers ADD PRIMARY KEY (connection_uri_domain, app_id, tenant_id, third_party_id); ALTER TABLE tenant_thirdparty_providers ADD FOREIGN KEY (connection_uri_domain, app_id, tenant_id) REFERENCES tenant_configs (connection_uri_domain, app_id, tenant_id) ON DELETE CASCADE; -- CREATE TABLE IF NOT EXISTS tenant_thirdparty_provider_clients ( connection_uri_domain VARCHAR(256) DEFAULT '', app_id VARCHAR(64) DEFAULT 'public', tenant_id VARCHAR(64) DEFAULT 'public', third_party_id VARCHAR(28) NOT NULL, client_type VARCHAR(64) NOT NULL DEFAULT '', client_id VARCHAR(256) NOT NULL, client_secret TEXT, scope TEXT, force_pkce BOOLEAN, additional_config TEXT ); ALTER TABLE tenant_thirdparty_provider_clients ADD PRIMARY KEY (connection_uri_domain, app_id, tenant_id, third_party_id, client_type); ALTER TABLE tenant_thirdparty_provider_clients ADD FOREIGN KEY (connection_uri_domain, app_id, tenant_id, third_party_id) REFERENCES tenant_thirdparty_providers (connection_uri_domain, app_id, tenant_id, third_party_id) ON DELETE CASCADE; -- Session CALL st_add_column_if_not_exists('session_info', 'app_id', 'VARCHAR(64)', 'NOT NULL DEFAULT \'public\'', @status_message); CALL st_add_column_if_not_exists('session_info', 'tenant_id', 'VARCHAR(64)', 'NOT NULL DEFAULT \'public\'', @status_message); ALTER TABLE session_info ADD PRIMARY KEY (app_id, tenant_id, session_handle); ALTER TABLE session_info ADD FOREIGN KEY (app_id, tenant_id) REFERENCES tenants (app_id, tenant_id) ON DELETE CASCADE; CREATE INDEX session_expiry_index ON session_info (expires_at); -- CALL st_add_column_if_not_exists('session_access_token_signing_keys', 'app_id', 'VARCHAR(64)', 'NOT NULL DEFAULT \'public\'', @status_message); ALTER TABLE session_access_token_signing_keys ADD PRIMARY KEY (app_id, created_at_time); ALTER TABLE session_access_token_signing_keys ADD FOREIGN KEY (app_id) REFERENCES apps (app_id) ON DELETE CASCADE; -- JWT CALL st_add_column_if_not_exists('jwt_signing_keys', 'app_id', 'VARCHAR(64)', 'NOT NULL DEFAULT \'public\'', @status_message); ALTER TABLE jwt_signing_keys ADD PRIMARY KEY (app_id, key_id); ALTER TABLE jwt_signing_keys ADD FOREIGN KEY (app_id) REFERENCES apps (app_id) ON DELETE CASCADE; -- EmailVerification CALL st_add_column_if_not_exists('emailverification_verified_emails', 'app_id', 'VARCHAR(64)', 'NOT NULL DEFAULT \'public\'', @status_message); ALTER TABLE emailverification_verified_emails ADD PRIMARY KEY (app_id, user_id, email); ALTER TABLE emailverification_verified_emails ADD FOREIGN KEY (app_id) REFERENCES apps (app_id) ON DELETE CASCADE; -- CALL st_add_column_if_not_exists('emailverification_tokens', 'app_id', 'VARCHAR(64)', 'NOT NULL DEFAULT \'public\'', @status_message); CALL st_add_column_if_not_exists('emailverification_tokens', 'tenant_id', 'VARCHAR(64)', 'NOT NULL DEFAULT \'public\'', @status_message); ALTER TABLE emailverification_tokens ADD PRIMARY KEY (app_id, tenant_id, user_id, email, token); ALTER TABLE emailverification_tokens ADD FOREIGN KEY (app_id, tenant_id) REFERENCES tenants (app_id, tenant_id) ON DELETE CASCADE; ALTER TABLE emailverification_tokens ADD CONSTRAINT token UNIQUE (token); CREATE INDEX emailverification_tokens_index ON emailverification_tokens(token_expiry); -- EmailPassword CALL st_add_column_if_not_exists('emailpassword_users', 'app_id', 'VARCHAR(64)', 'NOT NULL DEFAULT \'public\'', @status_message); ALTER TABLE emailpassword_users ADD PRIMARY KEY (app_id, user_id); ALTER TABLE emailpassword_users ADD FOREIGN KEY (app_id, user_id) REFERENCES app_id_to_user_id (app_id, user_id) ON DELETE CASCADE; -- -- CREATE TABLE IF NOT EXISTS emailpassword_user_to_tenant ( app_id VARCHAR(64) DEFAULT 'public', tenant_id VARCHAR(64) DEFAULT 'public', user_id CHAR(36) NOT NULL, email VARCHAR(256) NOT NULL ); ALTER TABLE emailpassword_user_to_tenant ADD PRIMARY KEY (app_id, tenant_id, user_id); ALTER TABLE emailpassword_user_to_tenant ADD CONSTRAINT email UNIQUE (app_id, tenant_id, email); ALTER TABLE emailpassword_user_to_tenant ADD CONSTRAINT FOREIGN KEY (app_id, tenant_id, user_id) REFERENCES all_auth_recipe_users (app_id, tenant_id, user_id) ON DELETE CASCADE; INSERT IGNORE INTO emailpassword_user_to_tenant (user_id, email) SELECT user_id, email FROM emailpassword_users; -- CALL st_add_column_if_not_exists('emailpassword_pswd_reset_tokens', 'app_id', 'VARCHAR(64)', 'NOT NULL DEFAULT \'public\'', @status_message); ALTER TABLE emailpassword_pswd_reset_tokens ADD PRIMARY KEY (app_id, user_id, token); ALTER TABLE emailpassword_pswd_reset_tokens ADD FOREIGN KEY (app_id, user_id) REFERENCES emailpassword_users (app_id, user_id) ON DELETE CASCADE; ALTER TABLE emailpassword_pswd_reset_tokens ADD CONSTRAINT token UNIQUE (token); CREATE INDEX emailpassword_password_reset_token_expiry_index ON emailpassword_pswd_reset_tokens (token_expiry); -- Passwordless CALL st_add_column_if_not_exists('passwordless_users', 'app_id', 'VARCHAR(64)', 'NOT NULL DEFAULT \'public\'', @status_message); ALTER TABLE passwordless_users ADD PRIMARY KEY (app_id, user_id); ALTER TABLE passwordless_users ADD FOREIGN KEY (app_id, user_id) REFERENCES app_id_to_user_id (app_id, user_id) ON DELETE CASCADE; -- CREATE TABLE IF NOT EXISTS passwordless_user_to_tenant ( app_id VARCHAR(64) DEFAULT 'public', tenant_id VARCHAR(64) DEFAULT 'public', user_id CHAR(36) NOT NULL, email VARCHAR(256), phone_number VARCHAR(256) ); ALTER TABLE passwordless_user_to_tenant ADD PRIMARY KEY (app_id, tenant_id, user_id); ALTER TABLE passwordless_user_to_tenant ADD CONSTRAINT email UNIQUE (app_id, tenant_id, email); ALTER TABLE passwordless_user_to_tenant ADD CONSTRAINT phone_number UNIQUE (app_id, tenant_id, phone_number); ALTER TABLE passwordless_user_to_tenant ADD FOREIGN KEY (app_id, tenant_id, user_id) REFERENCES all_auth_recipe_users (app_id, tenant_id, user_id) ON DELETE CASCADE; INSERT IGNORE INTO passwordless_user_to_tenant (user_id, email, phone_number) SELECT user_id, email, phone_number FROM passwordless_users; -- CALL st_add_column_if_not_exists('passwordless_devices', 'app_id', 'VARCHAR(64)', 'NOT NULL DEFAULT \'public\'', @status_message); CALL st_add_column_if_not_exists('passwordless_devices', 'tenant_id', 'VARCHAR(64)', 'NOT NULL DEFAULT \'public\'', @status_message); ALTER TABLE passwordless_devices ADD PRIMARY KEY (app_id, tenant_id, device_id_hash); ALTER TABLE passwordless_devices ADD FOREIGN KEY (app_id, tenant_id) REFERENCES tenants (app_id, tenant_id) ON DELETE CASCADE; CREATE INDEX passwordless_devices_email_index ON passwordless_devices (app_id, tenant_id, email); CREATE INDEX passwordless_devices_phone_number_index ON passwordless_devices (app_id, tenant_id, phone_number); -- CALL st_add_column_if_not_exists('passwordless_codes', 'app_id', 'VARCHAR(64)', 'NOT NULL DEFAULT \'public\'', @status_message); CALL st_add_column_if_not_exists('passwordless_codes', 'tenant_id', 'VARCHAR(64)', 'NOT NULL DEFAULT \'public\'', @status_message); ALTER TABLE passwordless_codes ADD PRIMARY KEY (app_id, tenant_id, code_id); ALTER TABLE passwordless_codes ADD FOREIGN KEY (app_id, tenant_id, device_id_hash) REFERENCES passwordless_devices (app_id, tenant_id, device_id_hash) ON DELETE CASCADE; ALTER TABLE passwordless_codes ADD CONSTRAINT link_code_hash UNIQUE (app_id, tenant_id, link_code_hash); CREATE INDEX passwordless_codes_created_at_index ON passwordless_codes (app_id, tenant_id, created_at); -- ThirdParty CALL st_add_column_if_not_exists('thirdparty_users', 'app_id', 'VARCHAR(64)', 'NOT NULL DEFAULT \'public\'', @status_message); ALTER TABLE thirdparty_users ADD PRIMARY KEY (app_id, user_id); ALTER TABLE thirdparty_users ADD FOREIGN KEY (app_id, user_id) REFERENCES app_id_to_user_id (app_id, user_id) ON DELETE CASCADE; CREATE INDEX thirdparty_users_thirdparty_user_id_index ON thirdparty_users (app_id, third_party_id, third_party_user_id); CREATE INDEX thirdparty_users_email_index ON thirdparty_users (app_id, email); -- CREATE TABLE IF NOT EXISTS thirdparty_user_to_tenant ( app_id VARCHAR(64) DEFAULT 'public', tenant_id VARCHAR(64) DEFAULT 'public', user_id CHAR(36) NOT NULL, third_party_id VARCHAR(28) NOT NULL, third_party_user_id VARCHAR(256) NOT NULL ); ALTER TABLE thirdparty_user_to_tenant ADD PRIMARY KEY (app_id, tenant_id, user_id); ALTER TABLE thirdparty_user_to_tenant ADD CONSTRAINT third_party_user_id UNIQUE (app_id, tenant_id, third_party_id, third_party_user_id); ALTER TABLE thirdparty_user_to_tenant ADD FOREIGN KEY (app_id, tenant_id, user_id) REFERENCES all_auth_recipe_users (app_id, tenant_id, user_id) ON DELETE CASCADE; INSERT IGNORE INTO thirdparty_user_to_tenant (user_id, third_party_id, third_party_user_id) SELECT user_id, third_party_id, third_party_user_id FROM thirdparty_users; -- UserIdMapping CALL st_add_column_if_not_exists('userid_mapping', 'app_id', 'VARCHAR(64)', 'NOT NULL DEFAULT \'public\'', @status_message); ALTER TABLE userid_mapping ADD PRIMARY KEY (app_id, supertokens_user_id, external_user_id); ALTER TABLE userid_mapping ADD CONSTRAINT supertokens_user_id UNIQUE (app_id, supertokens_user_id); ALTER TABLE userid_mapping ADD CONSTRAINT external_user_id UNIQUE (app_id, external_user_id); ALTER TABLE userid_mapping ADD FOREIGN KEY (app_id, supertokens_user_id) REFERENCES app_id_to_user_id (app_id, user_id) ON DELETE CASCADE; -- UserRoles CALL st_add_column_if_not_exists('roles', 'app_id', 'VARCHAR(64)', 'NOT NULL DEFAULT \'public\'', @status_message); ALTER TABLE roles ADD PRIMARY KEY (app_id, role); ALTER TABLE roles ADD FOREIGN KEY (app_id) REFERENCES apps (app_id) ON DELETE CASCADE; -- CALL st_add_column_if_not_exists('role_permissions', 'app_id', 'VARCHAR(64)', 'NOT NULL DEFAULT \'public\'', @status_message); ALTER TABLE role_permissions ADD PRIMARY KEY (app_id, role, permission); ALTER TABLE role_permissions ADD FOREIGN KEY (app_id, role) REFERENCES roles (app_id, role) ON DELETE CASCADE; CREATE INDEX role_permissions_permission_index ON role_permissions (app_id, permission); -- CALL st_add_column_if_not_exists('user_roles', 'app_id', 'VARCHAR(64)', 'NOT NULL DEFAULT \'public\'', @status_message); CALL st_add_column_if_not_exists('user_roles', 'tenant_id', 'VARCHAR(64)', 'NOT NULL DEFAULT \'public\'', @status_message); ALTER TABLE user_roles ADD PRIMARY KEY (app_id, tenant_id, user_id, role); ALTER TABLE user_roles ADD FOREIGN KEY (app_id, role) REFERENCES roles (app_id, role) ON DELETE CASCADE; ALTER TABLE user_roles ADD FOREIGN KEY (app_id, tenant_id) REFERENCES tenants (app_id, tenant_id) ON DELETE CASCADE; CREATE INDEX user_roles_role_index ON user_roles (app_id, tenant_id, role); -- UserMetadata CALL st_add_column_if_not_exists('user_metadata', 'app_id', 'VARCHAR(64)', 'NOT NULL DEFAULT \'public\'', @status_message); ALTER TABLE user_metadata ADD PRIMARY KEY (app_id, user_id); ALTER TABLE user_metadata ADD FOREIGN KEY (app_id) REFERENCES apps (app_id) ON DELETE CASCADE; -- Dashboard CALL st_add_column_if_not_exists('dashboard_users', 'app_id', 'VARCHAR(64)', 'NOT NULL DEFAULT \'public\'', @status_message); ALTER TABLE dashboard_users ADD PRIMARY KEY (app_id, user_id); ALTER TABLE dashboard_users ADD CONSTRAINT email UNIQUE (app_id, email); ALTER TABLE dashboard_users ADD FOREIGN KEY (app_id) REFERENCES apps (app_id) ON DELETE CASCADE; -- CALL st_add_column_if_not_exists('dashboard_user_sessions', 'app_id', 'VARCHAR(64)', 'NOT NULL DEFAULT \'public\'', @status_message); ALTER TABLE dashboard_user_sessions ADD PRIMARY KEY (app_id, session_id); ALTER TABLE dashboard_user_sessions ADD FOREIGN KEY (app_id, user_id) REFERENCES dashboard_users (app_id, user_id) ON DELETE CASCADE; CREATE INDEX dashboard_user_sessions_expiry_index ON dashboard_user_sessions (expiry); -- TOTP CALL st_add_column_if_not_exists('totp_users', 'app_id', 'VARCHAR(64)', 'NOT NULL DEFAULT \'public\'', @status_message); ALTER TABLE totp_users ADD PRIMARY KEY (app_id, user_id); ALTER TABLE totp_users ADD FOREIGN KEY (app_id) REFERENCES apps (app_id) ON DELETE CASCADE; -- CALL st_add_column_if_not_exists('totp_user_devices', 'app_id', 'VARCHAR(64)', 'NOT NULL DEFAULT \'public\'', @status_message); ALTER TABLE totp_user_devices ADD PRIMARY KEY (app_id, user_id, device_name); ALTER TABLE totp_user_devices ADD FOREIGN KEY (app_id, user_id) REFERENCES totp_users (app_id, user_id) ON DELETE CASCADE; -- CALL st_add_column_if_not_exists('totp_used_codes', 'app_id', 'VARCHAR(64)', 'NOT NULL DEFAULT \'public\'', @status_message); CALL st_add_column_if_not_exists('totp_used_codes', 'tenant_id', 'VARCHAR(64)', 'NOT NULL DEFAULT \'public\'', @status_message); ALTER TABLE totp_used_codes ADD PRIMARY KEY (app_id, tenant_id, user_id, created_time_ms); ALTER TABLE totp_used_codes ADD FOREIGN KEY (app_id, user_id) REFERENCES totp_users (app_id, user_id) ON DELETE CASCADE; ALTER TABLE totp_used_codes ADD FOREIGN KEY (app_id, tenant_id) REFERENCES tenants (app_id, tenant_id) ON DELETE CASCADE; CREATE INDEX totp_used_codes_expiry_time_ms_index ON totp_used_codes (app_id, tenant_id, expiry_time_ms); -- ActiveUsers CALL st_add_column_if_not_exists('user_last_active', 'app_id', 'VARCHAR(64)', 'NOT NULL DEFAULT \'public\'', @status_message); ALTER TABLE user_last_active ADD PRIMARY KEY (app_id, user_id); ALTER TABLE user_last_active ADD FOREIGN KEY (app_id) REFERENCES apps (app_id) ON DELETE CASCADE; -- Drop procedures DROP PROCEDURE st_drop_all_fkeys; DROP PROCEDURE st_drop_all_keys; DROP PROCEDURE st_drop_all_pkeys; DROP PROCEDURE st_drop_all_indexes; DROP PROCEDURE st_add_column_if_not_exists;
-
Start the new instance(s) of the core (version 6.0.0)
- Updated the
java-jwt
dependency version - Increases free Dashboard user count to 3
- Fixed creating JWTs using MongoDB if a key already exists
- Using an internal
SemVer
class to handle version numbers. This will make handling CDI version ranges easier. - Support for CDI version
2.21
- Removed POST
/recipe/handshake
- Added
useDynamicSigningKey
intocreateNewSession
(POST/recipe/session
), replacingaccess_token_signing_key_dynamic
used in CDI<=2.18 - Added
useStaticSigningKey
intocreateSignedJWT
(POST/recipe/jwt
) - Added
checkDatabase
intoverifySession
(POST/recipe/session/verify
), replacingaccess_token_blacklisting
used in CDI<=2.18 - Removed
idRefreshToken
,jwtSigningPublicKey
,jwtSigningPublicKeyExpiryTime
andjwtSigningPublicKeyList
from responses - Deprecated GET
/recipe/jwt/jwks
- Added GET
/.well-known/jwks.json
: a standard jwks
- Removed POST
- Added new access token version
- Uses standard prop names (i.e.:
sub
instead ofuserId
) - Contains the id of the signing key in the header (as
kid
) - Stores the user payload merged into the root level, instead of the
userData
prop
- Uses standard prop names (i.e.:
- Session handling function now throw if the user payload contains protected props (
sub
,iat
,exp
,sessionHandle
,refreshTokenHash1
,parentRefreshTokenHash1
,antiCsrfToken
)- A related exception type was added as
AccessTokenPayloadError
- A related exception type was added as
- Refactored the handling of signing keys
createNewSession
now takes auseStaticKey
parameter instead of depending on theaccess_token_signing_key_dynamic
config valuecreateJWTToken
now supports signing by a dynamic keygetSession
now takes acheckDatabase
parameter instead of using theaccess_token_blacklisting
config value- Updated plugin interface version to 2.21
access_token_signing_key_dynamic
is now deprecated, only used for requests with CDI<=2.18access_token_blacklisting
is now deprecated, only used for requests with CDI<=2.18- Renamed
access_token_signing_key_update_interval
toaccess_token_dynamic_signing_key_update_interval
- Added new
useStaticKey
field into session info - Manual migration is also required if
access_token_signing_key_dynamic
was set to false
- If using
access_token_signing_key_dynamic
false:ALTER TABLE session_info ADD COLUMN use_static_key BOOLEAN NOT NULL DEFAULT(true); ALTER TABLE session_info ALTER COLUMN use_static_key DROP DEFAULT;
INSERT INTO jwt_signing_keys(key_id, key_string, algorithm, created_at) select CONCAT('s-', created_at_time) as key_id, value as key_string, 'RS256' as algorithm, created_at_time as created_at from session_access_token_signing_keys;
- If using
access_token_signing_key_dynamic
true or not set: -
-
If using
access_token_signing_key_dynamic
false:db.session_info.update({}, { "$set": { "use_static_key": true } });
db.key_value.aggregate([ { "$match": { _id: "access_token_signing_key_list" } }, { $unwind: "$keys" }, { $addFields: { _id: { "$concat": [ "s-", { $convert: { input: "$keys.created_at_time", to: "string" } } ] }, "key_string": "$keys.value", "algorithm": "RS256", "created_at": "$keys.created_at_time", } }, { "$project": { "keys": 0, } }, { "$merge": { "into": "jwt_signing_keys", } } ]);
-
If using
access_token_signing_key_dynamic
true or not set:db.session_info.update({}, { "$set": { "use_static_key": false } });
- Add Optional Search Tags to Pagination API to enable dashboard search
GET /user/search/tags
retrieves the available search tags
- Add TOTP recipe
- Add new tables for TOTP recipe:
totp_users
that stores the users that have enabled TOTPtotp_user_devices
that stores devices (each device has its own secret) for each usertotp_used_codes
that stores used codes for each user. This is to implement rate limiting and prevent replay attacks.user_last_active
that stores the last active time for each user.
GET /users/count/active
to fetch the number of active users after the given timestamp.POST /recipe/totp/device
to create a new device as well as the user if it doesn't exist.POST /recipe/totp/device/verify
to verify a device. This is to ensure that the user has access to the device.POST /recipe/totp/verify
to verify a code and continue the login flow.PUT /recipe/totp/device
to update the name of a device. Name is just a string that the user can set to identify the device.GET /recipe/totp/device/list
to get all devices for a user.POST /recipe/totp/device/remove
to remove a device. If the user has no more devices, the user is also removed.
- Adds null check in email normalisation to fix: supertokens/supertokens-node#514
- Normalises email in all APIs in which email was not being normalised: #577
- Dashboard Recipe
- Support with CDI version
2.18
- Adds
dashboard_users
table - Adds
dashboard_user_sessions
table
- Adds feature flag, ee folder and APIs to add / remove license keys for enterprise features.
- Updates the type of
access_token_validity
in the CoreConfig fromint
tolong
- Update dependencies for security updates: #525
- Updates core routes to now allow for trailing slashes
- Adds request IP allow & deny list: #511
- Fixes bug related to implementationDependencies.json
- EmailPassword User migration API which allows you to import users with their email and password hashes.
- Support to import users with password hashes from Firebase
- Support with CDI version
2.16
- Hello API on
/
route.
-
Updates the
password_hash
column in theemailpassword_users
table fromVARCHAR(128)
toVARCHAR(256)
to support more password hash lengths. -
Updates the
third_party_user_id
column in thethirdparty_users
table fromVARCHAR(128)
toVARCHAR(256)
to resolve #306 -
For legacy users who are self hosting the SuperTokens core run the following command to update your database with the changes:
- With MySql:
ALTER TABLE thirdparty_users MODIFY third_party_user_id VARCHAR(256); ALTER TABLE emailpassword_users MODIFY password_hash VARCHAR(256);
- With PostgreSQL:
ALTER TABLE thirdparty_users ALTER COLUMN third_party_user_id TYPE VARCHAR(256); ALTER TABLE emailpassword_users ALTER COLUMN password_hash TYPE VARCHAR(256);
- With MySql:
- Updated java-jwt to handle
null
claims in JWTs
- Fixed handling of
null
in access token payloads: #499
- Changes logging level of API start / finished & Cronjob start / finished to be
INFO
level instead ofDEBUG
level. - Added new config
log_level
to set logging level. Possible values areDEBUG
|INFO
|WARN
|ERROR
|NONE
. As an example, setting the log level toWARN
would make the core print outWARN
andERROR
level logs.
- Updates UserIdMapping recipe to resolve UserId Mappings for Auth recipes in the core itself
- Adds UserIdMapping recipe
- Support for collecting and displaying failing tests
- Adds
userid_mapping
table
- Fixes
/recipe/session/user GET
to return only session handles that have not expired. - Support for new plugin interface version (v2.15)
- Checks for if the session has expired in
updateSession
before calling the update function.
- Adds UserRoles recipe
- Fixes base_path config option not being observed when running
supertokens list
- Adds base_path normalization logic
- Adds
roles
,role_permissions
anduser_roles
table
- Changed default
--with_argon2_hashing_pool_size
inhashingCalibrate
CLI command to 1.
- Adds github action for running tests against in memory db.
- Adds github action for checking if "Run tests" action was completed (to run in PRs)
- Fixes how config values are changed during tests.
- Adds 60 mins timeout to github action jobs
- Moves deleting user metadata to happen before deleting the actual user.
- Adds support for argon2 hashing.
- Adds colours to CLI output (in case of errors).
password_hashing_alg
argon2_iterations
argon2_memory_kb
argon2_parallelism
argon2_hashing_pool_size
bcrypt_log_rounds
supertokens hashingCalibrate
: Used to calibrate argon2 and bcrypt passing hashing params.
- Fixes memory leak during testing.
- Updated plugin interface version
- Adds usermetadata recipe
- Update CONTRIBUTING.md with instructions for gitpod setup
- Added
user_metadata
table
- Updated plugin interface version
- Fixed ResultSet instances to avoid Memory Leaks
- Fixed #373: Catching
StorageTransactionLogicException
in transaction helper function for retries
- Supporting CDI v2.12
- Adding the
userId
to the reponse ofrecipe/user/password/reset
- Adds support for providing base path for all APIs: supertokens/supertokens-node#252
- Add workflow to verify if pr title follows conventional commits
base_path
- default is""
(No base path)
- Added Passwordless recipe ( with unit test coverage )
- Adds new tables for passwordless:
passwordless_users
that stores the users of the passwordless recipepasswordless_devices
that stores devices/information about passwordless login attemptspasswordless_codes
that stores the codes each device can consume to finish the login process
- New recipeId in
/users
response with a corresponding new user type
- Delete user endpoint
- Issue with JWT expiry always being lower than expected
- Modulus and exponent for JsonWebKeys are now sent as unsigned when fetching public keys from the /jwt/jwks.json endpoint. Both values are url encoded without any padding.
- JWT creation logic to add a
iss
claim only if none is provided
- New config values
password_reset_token_lifetime
andemail_verification_token_lifetime
: #297 - Added support for multiple access token signing keys: #305
- Updated CDI version
- Added a table to store access token signing keys into SQL schema, called
session_access_token_signing_keys
- New JWT recipe to create JWT tokens using SuperTokens
- New table
jwt_signing_keys
added to store keys used by the JWT recipe
- Explicitly adds UTF-8 compatible conversion when encoding / decoding base64 strings.
- Issue with verifying refresh token throwing an unauthorised exception due to a db connection error.
- Sends far ahead jwt signing key expiry time in case updating them is disabled: #304
- Changes JWT signing key update interval to not be limited to 720 hours
- Logs non "OK" status code from APIs for debugging purposes.
- Always throws unauthorised response if refresh token is not valid - previously it was throwing a 500 error in case it was not properly base 64 encoded.
- Make emailverificaiton tables take a generic userId: #258
- Adds new count and pagination APIs: #259
- Adds new API to get session data, and deprecates older one to get session and JWT payload separately: #255
- Removed
isVerified
boolean from thirdparty sign in up API as per CDI spec 2.8, and hence does not do email verification in this API either. Also related to #295
- Add
GET /recipe/users/[email protected]
endpoint for ThirdParty recipe to fetch all users with given email - Add new emailverification APIs for remove tokens and unverify email.
- Add
PUT /recipe/user
for emailpassword recipe to change user's password or email.
NullPointerException
that is thrown inAccessTokenSigningKey.java
class when thekeyInfo
object is accessed in parallel after the signing key has expired: #282
test_mode
to the options for running the core so that it can be run in test mode whilst being tested by the backend SDK.- Adds
jwtSigningPublicKey
andjwtSigningPublicKeyExpiryTime
to API response when returningTRY_REFRESH_TOKEN
from session verify.
- Uses Open JDK 15.0.1
- Extracted email verification into its own recipe
- ThirdParty recipe API
- Normalises email by making it all lower case
- Changes in handshake API
- Changes in config
- Changes in session create, verify and refresh APis
- Used rowmapper for in memory db
- Adds email verification APIs
- Adds user pagination APIs
- Adds timeJoined to whenever a user object is returned from an API
- Makes Hello API do a db query as well for better status checking
- Changes as per CDI 2.4: supertokens/core-driver-interface#1
- In memory db uses the SQL interface
- Emailpassword recipe functions and APIs
- Deprecates the need for a separate SQLite repo (since the in mem one already exists within the core)
- Issue #84 - Correct access token signing key expiry not being sent by APIs
- Fixed issue of docker image hanging when run in foreground
- Updates the access token if blacklisting is switched on and the JWT payload has been changed somehow
- API key support
- JWT Api Key Rotation
- Compatibility with the inefficient method for handling refresh tokens.
- CSRF check in refresh API
- set csrf config to
false
by default - compatibility with CDI 2.3
- When regenerating session, uses old access tokens' parentRefreshTokenHash1 instead of null
- Optimises refresh token to not store old tokens in the database
- removes the need for a license key
- removes API Pings
- Makes default session expiry status code 401
- Makes default refresh API path "/session/refresh"
- Compatibility with CDI 2.2. Makes
cookie_domain
default value to not set, so that it will work with any API - Makes sameSite = lax by default
- If licenseKey is missing, then dependency jars are downloaded in DEV mode
- Makes license Apache 2.0
- Changes how versioning works to make it per API call.
- Supports CDI 2.1
- Forcing of no in memory database flag to start command on Linux fixed
- Uses in memory database in dev mode if database is not configured
- Removes the need to specify dev / production when running the start command
- Compatibility with CDI 2.0
- API versions
- SameSite cookie option
- Updating of JWT payload
- Session expired status code configuration
- Partial lmrt support
- Compatibility with NoSQL databases like MongoDB
- Setting sameSite cookie option. However, this is not usable in this release.
- Adds #!/bin/bash in scripts
- Allow for an unlimited number of SuperTokens instances in production mode
- License changes to reflect the above