Paid Feature
This is a paid feature.
For self hosted users, Sign up to get a license key and follow the instructions sent to you by email. Using the dev license key is free. We only start charging you once you enable the feature in production using the provided production license key.
For managed service users, you can click on the "enable paid features" button on our dashboard, and follow the steps from there on. Once enabled, this feature is free on the provided development environment.
TOTP required for all users
In this page, we will show you how to implement an MFA policy that requires all users to use TOTP before they get access to your application.
note
We assume that the first factor is email password or social login, but the same set of steps will be applicable for other first factor types as well.
#
Single tenant setup#
Backend setupTo start with, we configure the backend in the following way:
- NodeJS
- GoLang
- Python
- Other Frameworks
Important
import supertokens from "supertokens-node";
import ThirdParty from "supertokens-node/recipe/thirdparty"
import EmailPassword from "supertokens-node/recipe/emailpassword"
import MultiFactorAuth from "supertokens-node/recipe/multifactorauth"
import totp from "supertokens-node/recipe/totp"
import Session from "supertokens-node/recipe/session"
supertokens.init({
supertokens: {
connectionURI: "..."
},
appInfo: {
appName: "...",
apiDomain: "...",
websiteDomain: "..."
},
recipeList: [
Session.init(),
ThirdParty.init({
//...
}),
EmailPassword.init({
//...
}),
totp.init(),
MultiFactorAuth.init({
firstFactors: [
MultiFactorAuth.FactorIds.EMAILPASSWORD,
MultiFactorAuth.FactorIds.THIRDPARTY
],
override: {
functions: (originalImplementation) => {
return {
...originalImplementation,
getMFARequirementsForAuth: async function (input) {
return [MultiFactorAuth.FactorIds.TOTP]
}
}
}
}
})
]
})
note
Coming soon.
note
Coming soon.
- Notice that we have initialised the TOTP recipe in the
recipeList
. By default, no configs are required for it, but you can provide:issuer
: This is the name that will show up in the TOTP app for the user. By default, this is equal to theappName
config, however, you can change it to something else using this property.defaultSkew
: The default value of this is1
, which means that TOTP codes that were generated 1 tick before, and that will be generated 1 tick after from the current tick will be accepted at any given time (including the TOTP of the current tick, of course).defaultPeriod
: The default value of this is30
, which means that the current tick is valie for 30 seconds. So by default, a TOTP code that's just shown to the user, is valid for 60 seconds (defaultPeriod + defaultSkew*defaultPeriod
seconds)
- We also override the
getMFARequirementsForAuth
function to indicate thattotp
must be completed before the user can access the app. Notice that we do not check for the userId there, and returntotp
for all users.
Once the user finishes the first factor (for example, with emailpassword), their session access token payload will look like this:
{
"st-mfa": {
"c": {
"emailpassword": 1702877939,
},
"v": false
}
}
The v
being false
indicates that there are still factors that are pending. After the user has finished totp, the payload will look like:
{
"st-mfa": {
"c": {
"emailpassword": 1702877939,
"totp": 1702877999
},
"v": true
}
}
Indicating that the user has finished all required factors, and should be allowed to access the app.
#
Frontend setupWe start by modifying the init
function call on the frontend like so:
- ReactJS
- Angular
- Vue
You will have to make changes to the auth route config, as well as to the supertokens-web-js
SDK config at the root of your application:
This change is in your auth route config.
// this goes in the auth route config of your frontend app (once the pre built UI script has been loaded)
(window as any).supertokensUIInit("supertokensui", {
appInfo: {
appName: "...",
apiDomain: "...",
websiteDomain: "...",
},
recipeList: [
// other recipes..
(window as any).supertokensUITOTP.init(),
(window as any).supertokensUIMultiFactorAuth.init({
firstFactors: [
(window as any).supertokensUIMultiFactorAuth.FactorIds.EMAILPASSWORD,
(window as any).supertokensUIMultiFactorAuth.FactorIds.THIRDPARTY
]
})
]
})
This change goes in the supertokens-web-js
SDK config at the root of your application:
import SuperTokens from 'supertokens-web-js';
import MultiFactorAuth from 'supertokens-web-js/recipe/multifactorauth';
import Totp from "supertokens-web-js/recipe/totp";
SuperTokens.init({
appInfo: {
apiDomain: "...",
apiBasePath: "...",
appName: "...",
},
recipeList: [
// other recipes...
MultiFactorAuth.init(),
Totp.init()
],
});
import supertokens from "supertokens-auth-react"
import Passwordless from "supertokens-auth-react/recipe/passwordless"
import MultiFactorAuth from "supertokens-auth-react/recipe/multifactorauth"
import totp from "supertokens-auth-react/recipe/totp"
supertokens.init({
appInfo: {
appName: "...",
apiDomain: "...",
websiteDomain: "...",
},
recipeList: [
// other recipes..
totp.init(),
MultiFactorAuth.init({
firstFactors: [
MultiFactorAuth.FactorIds.EMAILPASSWORD,
MultiFactorAuth.FactorIds.THIRDPARTY
]
})
]
})
You will have to make changes to the auth route config, as well as to the supertokens-web-js
SDK config at the root of your application:
This change is in your auth route config.
// this goes in the auth route config of your frontend app (once the pre built UI script has been loaded)
(window as any).supertokensUIInit("supertokensui", {
appInfo: {
appName: "...",
apiDomain: "...",
websiteDomain: "...",
},
recipeList: [
// other recipes..
(window as any).supertokensUITOTP.init(),
(window as any).supertokensUIMultiFactorAuth.init({
firstFactors: [
(window as any).supertokensUIMultiFactorAuth.FactorIds.EMAILPASSWORD,
(window as any).supertokensUIMultiFactorAuth.FactorIds.THIRDPARTY
]
})
]
})
This change goes in the supertokens-web-js
SDK config at the root of your application:
import SuperTokens from 'supertokens-web-js';
import MultiFactorAuth from 'supertokens-web-js/recipe/multifactorauth';
import Totp from "supertokens-web-js/recipe/totp";
SuperTokens.init({
appInfo: {
apiDomain: "...",
apiBasePath: "...",
appName: "...",
},
recipeList: [
// other recipes...
MultiFactorAuth.init(),
Totp.init()
],
});
- Just like on the backend, we init the
totp
recipe in therecipeList
. - We also init the
MultiFactorAuth
recipe, and pass in the first factors that we want to use. In this case, that would beemailpassword
andthirdparty
- same as the backend.
Next, we need to add the TOTP pre built UI when rendering the SuperTokens component:
- ReactJS
- Angular
- Vue
success
This step is not required for non React apps, since all the pre built UI components are already added into the bundle.
success
This step is not required for non React apps, since all the pre built UI components are already added into the bundle.
With the above configuration, users will see emailpassword or social login UI when they visit the auth page. After completing that, users will be redirected to /auth/mfa/totp
(assuming that the websiteBasePath
is /auth
) where they will be asked to setup the factor, or complete the TOTP challenge if they have already setup the factor before. The UI for this screen looks like:
- Factor Setup UI
- Verification UI (In case the factor is already setup before).
#
Multi tenant setupIn a multi tenancy setup, you may want to enable TOTP for all users, across all tenants, or for all users within specific tenants. For enabling for all users across all tenants, it's the same steps as in the single tenant setup section above, so in this section, we will focus on enabling TOTP for all users within specific tenants.
#
Backend setupTo start, we will initialise the TOTP and the MultiFactorAuth recipes in the following way:
- NodeJS
- GoLang
- Python
- Other Frameworks
Important
import supertokens from "supertokens-node";
import ThirdParty from "supertokens-node/recipe/thirdparty"
import EmailPassword from "supertokens-node/recipe/emailpassword"
import MultiFactorAuth from "supertokens-node/recipe/multifactorauth"
import totp from "supertokens-node/recipe/totp"
import Session from "supertokens-node/recipe/session"
supertokens.init({
supertokens: {
connectionURI: "..."
},
appInfo: {
appName: "...",
apiDomain: "...",
websiteDomain: "..."
},
recipeList: [
Session.init(),
ThirdParty.init({
//...
}),
EmailPassword.init({
//...
}),
totp.init(),
MultiFactorAuth.init()
]
})
note
Coming soon.
note
Coming soon.
Unlike the single tenant setup, we do not provide any config to the MultiFactorAuth
recipe cause all the necessary configuration will be done on a tenant level.
- Dashboard
- NodeJS
- GoLang
- Python
- cURL
Important
To configure TOTP requirement for a tenant, we can call the following API:
import Multitenancy from "supertokens-node/recipe/multitenancy";
import MultiFactorAuth from "supertokens-node/recipe/multifactorauth"
async function createNewTenant() {
let resp = await Multitenancy.createOrUpdateTenant("customer1", {
firstFactors: [
MultiFactorAuth.FactorIds.EMAILPASSWORD,
MultiFactorAuth.FactorIds.THIRDPARTY
],
requiredSecondaryFactors: [MultiFactorAuth.FactorIds.TOTP]
});
if (resp.createdNew) {
// Tenant created successfully
} else {
// Existing tenant's config was modified.
}
}
- In the above, we set the
firstFactors
to["emailpassword", "thirdparty"]
to indicate that the first factor can be either emailpassword or thirdparty. - We set the
requiredSecondaryFactors
to["totp"]
to indicate that TOTP is required for all users in this tenant. The default implementation ofgetMFARequirementsForAuth
in theMultiFactorAuth
takes this into account.
note
Coming soon.
note
Coming soon.
To configure TOTP requirement for a tenant, we can call the following API:
- Single app setup
- Multi app setup
curl --location --request PUT '/recipe/multitenancy/tenant/v2' \
--header 'api-key: ' \
--header 'Content-Type: application/json' \
--data-raw '{
"tenantId": "customer1",
"firstFactors": ["emailpassword", "thirdparty"],
"requiredSecondaryFactors": ["totp"]
}'
curl --location --request PUT '/recipe/multitenancy/tenant/v2' \
--header 'api-key: ' \
--header 'Content-Type: application/json' \
--data-raw '{
"tenantId": "customer1",
"firstFactors": ["emailpassword", "thirdparty"],
"requiredSecondaryFactors": ["totp"]
}'
- In the above, we set the
firstFactors
to["emailpassword", "thirdparty"]
to indicate that the first factor can be either emailpassword or thirdparty. - We set the
requiredSecondaryFactors
to["totp"]
to indicate that TOTP is required for all users in this tenant. The default implementation ofgetMFARequirementsForAuth
in theMultiFactorAuth
takes this into account.
As shown above, enable Email Password and Third Party in the Login methods section and enable TOTP in the Secondary Factors Section.
Once the user finishes the first factor (for example, with emailpassword), their session access token payload will look like this:
{
"st-mfa": {
"c": {
"emailpassword": 1702877939,
},
"v": false
}
}
The v
being false
indicates that there are still factors that are pending. After the user has finished totp, the payload will look like:
{
"st-mfa": {
"c": {
"emailpassword": 1702877939,
"totp": 1702877999
},
"v": true
}
}
Indicating that the user has finished all required factors, and should be allowed to access the app.
#
Frontend setupWe start by modifying the init
function call on the frontend like so:
- ReactJS
- Angular
- Vue
You will have to make changes to the auth route config, as well as to the supertokens-web-js
SDK config at the root of your application:
This change is in your auth route config.
// this goes in the auth route config of your frontend app (once the pre built UI script has been loaded)
(window as any).supertokensUIInit("supertokensui", {
appInfo: {
appName: "...",
apiDomain: "...",
websiteDomain: "...",
},
usesDynamicLoginMethods: true,
recipeList: [
// other recipes...
(window as any).supertokensUITOTP.init(),
(window as any).supertokensUIMultiFactorAuth.init(),
(window as any).supertokensUIMultitenancy.init({
override: {
functions: (originalImplementation) => {
return {
...originalImplementation,
getTenantId: async (context) => {
return "TODO"
}
}
}
}
})
]
})
This change goes in the supertokens-web-js
SDK config at the root of your application:
supertokens.init({
appInfo: {
apiDomain: "...",
apiBasePath: "...",
appName: "...",
},
recipeList: [
Session.init(),
MultiFactorAuth.init(),
],
});
import supertokens from "supertokens-auth-react"
import MultiFactorAuth from "supertokens-auth-react/recipe/multifactorauth"
import totp from "supertokens-auth-react/recipe/totp"
import Multitenancy from "supertokens-auth-react/recipe/multitenancy"
supertokens.init({
appInfo: {
appName: "...",
apiDomain: "...",
websiteDomain: "...",
},
usesDynamicLoginMethods: true,
recipeList: [
// other recipes...
totp.init(),
MultiFactorAuth.init(),
Multitenancy.init({
override: {
functions: (originalImplementation) => {
return {
...originalImplementation,
getTenantId: async (context) => {
return "TODO"
}
}
}
}
})
]
})
You will have to make changes to the auth route config, as well as to the supertokens-web-js
SDK config at the root of your application:
This change is in your auth route config.
// this goes in the auth route config of your frontend app (once the pre built UI script has been loaded)
(window as any).supertokensUIInit("supertokensui", {
appInfo: {
appName: "...",
apiDomain: "...",
websiteDomain: "...",
},
usesDynamicLoginMethods: true,
recipeList: [
// other recipes...
(window as any).supertokensUITOTP.init(),
(window as any).supertokensUIMultiFactorAuth.init(),
(window as any).supertokensUIMultitenancy.init({
override: {
functions: (originalImplementation) => {
return {
...originalImplementation,
getTenantId: async (context) => {
return "TODO"
}
}
}
}
})
]
})
This change goes in the supertokens-web-js
SDK config at the root of your application:
supertokens.init({
appInfo: {
apiDomain: "...",
apiBasePath: "...",
appName: "...",
},
recipeList: [
Session.init(),
MultiFactorAuth.init(),
],
});
- Just like on the backend, we init the
totp
recipe in therecipeList
. - We also init the
MultiFactorAuth
recipe. Notice that unlike the single tenant setup, we do not specify thefirstFactors
here. That information is fetched based on the tenantId you provide the SDK with. - We have set
usesDynamicLoginMethods: true
so that the SDK knows to fetch the login methods dynamically based on the tenantId. - Finally, we init the multi tenancy recipe and provide a method for getting the tenantId.
Next, we need to add the TOTP pre built UI when rendering the SuperTokens component:
- ReactJS
- Angular
- Vue
success
This step is not required for non React apps, since all the pre built UI components are already added into the bundle.
success
This step is not required for non React apps, since all the pre built UI components are already added into the bundle.
With the above configuration, users will see the first and second factor based on the tenant configuration. For the tenant we configured above, users will see email password or social login first. After completing that, users will be redirected to /auth/mfa/totp
(assuming that the websiteBasePath
is /auth
) where they will be asked to setup the factor, or complete the TOTP challenge if they have already setup the factor before. The UI for this screen looks like:
- Factor Setup UI
- Verification UI (In case the factor is already setup before).
#
Protecting frontend and backend routesSee the section on protecting frontend and backend routes.
#
Frontend events, pre and post API hooksTODO..