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.
Frontend setup
#
Single tenant setupThe pre built UI provides support for the following MFA methods:
- TOTP
- Email / phone OTP
If you want other types of MFA (like magic links, or password), please consider checking out the custom UI second.
We start by initialising the MFA recipe on the frontend and providing the list of first factors as shown below:
- 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: [
(window as any).supertokensUIEmailPassword.init( /* ... */),
(window as any).supertokensUIPasswordless.init({
contactMethod: "EMAIL_OR_PHONE",
}),
(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 Session from 'supertokens-web-js/recipe/session'
SuperTokens.init({
appInfo: {
apiDomain: "...",
apiBasePath: "...",
appName: "...",
},
recipeList: [
Session.init(),
MultiFactorAuth.init()
],
});
import supertokens from "supertokens-auth-react"
import EmailPassword from "supertokens-auth-react/recipe/emailpassword"
import Passwordless from "supertokens-auth-react/recipe/passwordless"
import MultiFactorAuth from "supertokens-auth-react/recipe/multifactorauth"
supertokens.init({
appInfo: {
appName: "...",
apiDomain: "...",
websiteDomain: "...",
},
recipeList: [
EmailPassword.init( /* ... */),
Passwordless.init({
contactMethod: "EMAIL_OR_PHONE",
}),
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: [
(window as any).supertokensUIEmailPassword.init( /* ... */),
(window as any).supertokensUIPasswordless.init({
contactMethod: "EMAIL_OR_PHONE",
}),
(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 Session from 'supertokens-web-js/recipe/session'
SuperTokens.init({
appInfo: {
apiDomain: "...",
apiBasePath: "...",
appName: "...",
},
recipeList: [
Session.init(),
MultiFactorAuth.init()
],
});
In the above snippet, we have configured thirdparty and email password as first factors. The second factor is configured on the backend, and is determined based on the boolean value of v
in the MFA claim in the session. If the v
is false
in the session, it means that there are still factors pending before the user has completed login. In this case, the frontend SDK calls the MFAInfo endpoint (see more about this later) on the backend which returns the list of factors (string[]
) that the user must complete next. For example:
- If the next array is
["otp-email"]
, then we will show the user the enter OTP screen for the email associated with the first factor login. - If the
n
array has multiple items, we will show the user a factor chooser screen using which they can decide which factor they want to continue with. - If the
next
is empty, it means that:- There is a misconfig on the backend. This would show an access denied screen to the user. OR;
- There is another claim that needs to be finished first (like email verification), before the next MFA challenge can be shown. This can happen if you configure the backend's
checkAllowedToSetupFactorElseThrowInvalidClaimError
function to not allow a factor setup until the email is verified.
If you notice, in the above code snippet, we have added Passwordless.init
as well, and this handles cases where the second factor is otp-email
or otp-sms
. For TOTP, we have a different recipe as shown later in this guide.
#
Multi tenant setupFor a multi factor setup, the first factors is decided based on the configuration of the tenant. Each tenant has a firstFactors
array configuration which will determine the login options shown for that tenant. For MFA, the login options will be determined by the requiredSecondaryFactors
config on the tenant, or based on the customisations for getMFARequirementsForAuth
on the backend.
To tell the frontend to dynamically load the factors based on the tenant, we need to give it four things:
- The current tenantId
- Enable dynamic login methods
- Add
MultiFactorAuth.init
to the recipe list without any configuredfirstFactors
- Init all the recipes that can be possibly used by any tenant as the first or second factor.
- 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: [
(window as any).supertokensUIMultitenancy.init({
override: {
functions: (oI) => {
return {
...oI,
getTenantId: (input) => {
// Implement the following based on the UX flow you want for
// tenant discovery
return "TODO.."
}
}
}
}
}),
(window as any).supertokensUIEmailPassword.init( /* ... */),
(window as any).supertokensUIPasswordless.init({
contactMethod: "EMAIL_OR_PHONE",
}),
(window as any).supertokensUIMultiFactorAuth.init()
]
})
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 Session from 'supertokens-web-js/recipe/session'
SuperTokens.init({
appInfo: {
apiDomain: "...",
apiBasePath: "...",
appName: "...",
},
recipeList: [
Session.init(),
MultiFactorAuth.init()
],
});
import supertokens from "supertokens-auth-react"
import EmailPassword from "supertokens-auth-react/recipe/emailpassword"
import Passwordless from "supertokens-auth-react/recipe/passwordless"
import MultiFactorAuth from "supertokens-auth-react/recipe/multifactorauth"
import Multitenancy from "supertokens-auth-react/recipe/multitenancy"
supertokens.init({
appInfo: {
appName: "...",
apiDomain: "...",
websiteDomain: "...",
},
usesDynamicLoginMethods: true,
recipeList: [
Multitenancy.init({
override: {
functions: (oI) => {
return {
...oI,
getTenantId: (input) => {
// Implement the following based on the UX flow you want for
// tenant discovery
return "TODO.."
}
}
}
}
}),
EmailPassword.init( /* ... */),
Passwordless.init({
contactMethod: "EMAIL_OR_PHONE",
}),
MultiFactorAuth.init()
]
})
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: [
(window as any).supertokensUIMultitenancy.init({
override: {
functions: (oI) => {
return {
...oI,
getTenantId: (input) => {
// Implement the following based on the UX flow you want for
// tenant discovery
return "TODO.."
}
}
}
}
}),
(window as any).supertokensUIEmailPassword.init( /* ... */),
(window as any).supertokensUIPasswordless.init({
contactMethod: "EMAIL_OR_PHONE",
}),
(window as any).supertokensUIMultiFactorAuth.init()
]
})
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 Session from 'supertokens-web-js/recipe/session'
SuperTokens.init({
appInfo: {
apiDomain: "...",
apiBasePath: "...",
appName: "...",
},
recipeList: [
Session.init(),
MultiFactorAuth.init()
],
});
- In the above code snippet, we have added
ThirdPartyEmailPassword
andPasswordless
as the auth methods. This works for a variety of use cases like:- The first factor for any tenant can be third party or email password login, and the second factor can be passwordless login (
otp-email
orotp-sms
). - The first factor for any tenant can be email password, with, or without a second factor (like
otp-email
).. - The first factor for any tenant can be third party, with, or without a second factor (like
otp-email
).. - The first factor for any tenant can be passwordless login (with magic link), with or without a second factor (like
otp-email
). - You can even change
passwordles.init
to usingthirdpartypasswordless.init
if you want to have the first factor for any tenant to be thirdparty or passwordless login, with or without a second factor (likeotp-email
).
- The first factor for any tenant can be third party or email password login, and the second factor can be passwordless login (
- We have initialised
MultiFactorAuth
without any configuredfirstFactors
because we want the frontend to dynamically load the first factors based on the tenant. Therefore, we have also setusesDynamicLoginMethods: true
in thesupertokens.init
call. - We have initialised
Multitenancy
as well, and provided a skeleton forgetTenantId
. You need to implement this function based on the UX flow you want for tenant discovery. For examlpe, here is a common UX flow in which we decide the tenant ID based on the current sub domain.
important
- If you do initialise the
firstFactors
array forMultiFactorAuth.init()
on the frontend, it will be ignored whenusesDynamicLoginMethods: true
is set. - If the tenant doesn't have the
firstFactors
array set, then the list of first factors that are rendered would be based on the login methods that are enabled in that tenant's config.
The second factor for a tenant is determined based on the secondaryFactors
config for the tenant, or based on any custom implementation for the getMFARequirementsForAuth
function. If the current user has specific MFA methods enabled for them, those will also be shown as options as well. Overall, the list of secondary factors will be used to build the next
array returned from the MFAInfo endpoint (see more about this later). For example:
- If the next array is
["otp-email"]
, then we will show the user the enter OTP screen for the email associated with the first factor login. - If the
n
array has multiple items, we will show the user a factor chooser screen using which they can decide which factor they want to continue with. - If the
next
is empty, it means that:- There is a misconfig on the backend. This would show an access denied screen to the user. OR;
- There is another claim that needs to be finished first (like email verification), before the next MFA challenge can be shown. This can happen if you configure the backend's
checkAllowedToSetupFactorElseThrowInvalidClaimError
function to not allow a factor setup until the email is verified.
In the subsequent sections, we will walk through specific MFA setup examples for your reference.
#
Handling misconfigurationsThere can be situations of misconfigurations. For example you may have enabled otp-email
for a user as a secondary factor, but did not add Passwordless
(or ThirdPartyPasswordless
) in the recipeList
on the frontend. In such (and similar) situations, the pre built UI on the frontend will throw an error which will be propagated to the error boundry of your app. The way to solve these errors is to recheck the recipeList
on the frontend, and make sure that it has all the recipes initialised that are needed for any factor configured on the backend.
#
The access denied screenSometimes, users may end up seeing an access denied screen during the login flow. This is shown if there is a 500 (backend sends a 500 status code) error during the MFA flow for API calls that are initiated automatically (without user action). For example:
- When the user wants to setup a new TOTP device, the pre bult UI calls the
createDevice
function from the totp recipe on page load, and if that fails, users will see the access denied screen asking them to retry. - When the user needs to complete an OTP email factor, and if the API call to send an email (which is called on page load) fails, then users will see the access denied screen asking them to retry.
You can override this component in the following way:
- ReactJS
- Angular
- Vue
caution
You cannot override the pre built UI in non react apps as of yet.
caution
You cannot override the pre built UI in non react apps as of yet.
#
Reference section#
MFA Info endpointThis is an important endpoint which can be used to:
- Know which factors are pending for the user (referred to as the the
next
array in our docs). - Update the
v
andc
values in the MFA claim. - Get a list of all factors that are already setup for the session user.
- For each factor, get a list of emails / phone numbers that can be used for that factor.
Our pre built UI uses this API automatically, but you can also always call this API manually if you are building a custom UI:
- Web
- Mobile
- Via NPM
- Via Script Tag
import MultifactorAuth from "supertokens-web-js/recipe/multifactorauth"
import Session from "supertokens-web-js/recipe/session"
async function fetchMFAInfo() {
if (await Session.doesSessionExist()) {
try {
let mfaInfo = await MultifactorAuth.resyncSessionAndFetchMFAInfo()
let factorEmails = mfaInfo.emails;
let factorPhoneNumbers = mfaInfo.phoneNumbers;
let emailsForOTPEmail = factorEmails["otp-email"];
let phoneNumbersForOTPSms = factorEmails["otp-sms"];
let isTotpSetup = mfaInfo.factors.alreadySetup.includes("totp");
let isOTPEmailSetup = mfaInfo.factors.alreadySetup.includes("otp-email");
let isOTPSmsSetup = mfaInfo.factors.alreadySetup.includes("otp-sms");
let next = mfaInfo.factors.next;
let factorsAllowedToBeSetup = mfaInfo.factors.allowedToSetup;
} catch (err: any) {
if (err.isSuperTokensGeneralError === true) {
// this may be a custom error message sent from the API by you.
window.alert(err.message);
} else {
window.alert("Oops! Something went wrong.");
}
}
} else {
throw new Error("Illegal function call: For first factor setup, you do not need to call this function")
}
}
async function fetchMFAInfo() {
if (await supertokensSession.doesSessionExist()) {
try {
let mfaInfo = await supertokensMultiFactorAuth.resyncSessionAndFetchMFAInfo()
let factorEmails = mfaInfo.emails;
let factorPhoneNumbers = mfaInfo.phoneNumbers;
let emailsForOTPEmail = factorEmails["otp-email"];
let phoneNumbersForOTPSms = factorEmails["otp-sms"];
let isTotpSetup = mfaInfo.factors.alreadySetup.includes("totp");
let isOTPEmailSetup = mfaInfo.factors.alreadySetup.includes("otp-email");
let isOTPSmsSetup = mfaInfo.factors.alreadySetup.includes("otp-sms");
let next = mfaInfo.factors.next;
let factorsAllowedToBeSetup = mfaInfo.factors.allowedToSetup;
} catch (err: any) {
if (err.isSuperTokensGeneralError === true) {
// this may be a custom error message sent from the API by you.
window.alert(err.message);
} else {
window.alert("Oops! Something went wrong.");
}
}
} else {
throw new Error("Illegal function call: For first factor setup, you do not need to call this function")
}
}
- In the above code snippet, we fetch the list of factors which the user must complete next (in the
next
array) and also all the relevant information to know what state each factor is in so that we can decide if we should ask the user to setup the factor (for example create a new TOTP device), or just ask them to solve the auth challenge instead (for example, showing the enter TOTP screen). - The function is called
resyncSessionAndFetchMFAInfo
because it does two things:- fetches the MFA info that you can consume to know the
next
array and what state each factor is in. - resyncs the value of the
v
andc
in the session's MFA claim.
- fetches the MFA info that you can consume to know the
Call the following API when you want to know the status of any factor. Notice that the API call requires the session's access token as an input (this should be added by our frontend SDK automatically):
curl --location --request PUT '<YOUR_API_DOMAIN>/auth/mfa/info' \
--header 'Authorization: Bearer ...'
- In the above code snippet, we fetch the list of factors which the user must complete next (in the
next
array) and also all the relevant information to know what state each factor is in so that we can decide if we should ask the user to setup the factor (for example create a new TOTP device), or just ask them to solve the auth challenge instead (for example, showing the enter TOTP screen). - The API is
PUT
and notGET
because it does two things:- fetches the MFA info that you can consume to know the
next
array and what state each factor is in. - resyncs the value of the
v
andc
in the session's MFA claim.
- fetches the MFA info that you can consume to know the
The struture of the raw JSON response is as follows:
{
"status": "OK",
"factors": {
"alreadySetup": ["totp", "otp-email", "..."],
"allowedToSetup": ["otp-sms", "otp-email", "..."],
"next": ["otp-sms", "..."]
},
"emails": {
"otp-email": ["user1@example.com", "user2@example.com"],
"link-email": ["user1@example.com", "user2@example.com"],
},
"phoneNumbers": {
"otp-sms": ["+1234567890", "+1098765432"],
"link-phone": ["+1234567890", "+1098765432"],
},
}factors.alreadySetup
is an array that contains all factors that have been setup by the user. If the current factor is a part of this array, it means that you can directly take the user to the factor challenge screen. If your factor depends on an email or phone number (like in the case ofotp-sms
orotp-email
), then you can find the email to send the code to in theemails
orphoneNumbers
object in the response with the key as the current factor ID.factors.allowedToSetup
is an array that contains all factors that the user is allowed to setup at this point in time. This is not that useful during the sign in process, but may be useful post sign in if you want to know what are the factors that the user can setup at any point in time.emails
is an object in which the key are all the factor IDs support by SuperTokens (and any custom factor ID added by you). The values against each of the keys is a list of emails that can be used to complete the factor. The first email (inddex 0) in the list is the preferred email to use for the factor. We calculate the order based on the first factor chosen by the user, and if the factor was already setup or not.If the array is empty, it means that there is no email associated with the user for that factor. This can happen only if the factor was not already setup. In this case, you should take the user to a screen to ask them to first enter an email, and then to the challenge screen.
We will further go into this flow in our common flows guide later on.
phoneNumbers
is similar to theemails
object, except that it contains phone numbers for factors that are based on phone numbers.The
factors.next
array determines the list of factors which the user must completed next. For example:- If the next array is
["otp-email"]
, then we will show the user the enter OTP screen for the email associated with the first factor login. - If the
n
array has multiple items:- For the pre built UI, we will show the user a factor chooser screen using which they can decide which factor they want to continue with.
- For custom UI, you would need to make this screen on your own.
- If the
next
is empty, it means that:- There is a misconfig on the backend. This would show an access denied screen to the user. OR;
- There is another claim that needs to be finished first (like email verification), before the next MFA challenge can be shown. This can happen if you configure the backend's
checkAllowedToSetupFactorElseThrowInvalidClaimError
function to not allow a factor setup until the email is verified.
- If the next array is
#
Frontend event hooksThe pre built UI emits a few events that you can listen to on the frontend. As an example, you can use these for analytics:
- ReactJS
- Angular
- Vue
// 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: [
(window as any).supertokensUIPasswordless.init({
contactMethod: "EMAIL_OR_PHONE",
onHandleEvent: (context) => {
if (context.action === "PASSWORDLESS_CODE_SENT") {
// this event is fired when the user has successfully sent out an OTP email / SMS
} else if (context.action === "PASSWORDLESS_RESTART_FLOW") {
// This event is fired when the user's OTP has expired, or
// they have reached the max limit of number of failed OTP attempts.
} else if (context.action === "SUCCESS" && !context.createdNewSession) {
// this event is fired when successfully completing the OTP email / SMS challenge
// and if it's not used in first factor (cause we do !context.createdNewSession)
}
}
}),
(window as any).supertokensUITOTP.init({
onHandleEvent: (context) => {
if (context.action === "TOTP_DEVICE_CREATED") {
// this event is fired during factor setup, when the user has successfully created the TOTP device. They still have to verify it by entering the TOTP.
} else if (context.action === "TOTP_DEVICE_VERIFIED") {
// this event is fired during factor setup, when the user has successfully verified the TOTP device
} else if (context.action === "TOTP_CODE_VERIFIED") {
// this event is fired when the user has successfully verified the TOTP code
// marking the TOTP factor as completed
}
}
}),
(window as any).supertokensUIMultiFactorAuth.init({
firstFactors: [/*...*/],
onHandleEvent: (context) => {
if (context.action === "FACTOR_CHOOSEN") {
let chosenFactorId = context.factorId;
// this event is fired when the user is shown the screen for
// picking one factor out of a choice of multiple factors
}
}
})
]
})
import SuperTokens from "supertokens-auth-react"
import MultiFactorAuth from "supertokens-auth-react/recipe/multifactorauth"
import TOTP from "supertokens-auth-react/recipe/totp";
import Passwordless from "supertokens-auth-react/recipe/passwordless";
SuperTokens.init({
appInfo: {
appName: "...",
apiDomain: "...",
websiteDomain: "...",
},
recipeList: [
Passwordless.init({
contactMethod: "EMAIL_OR_PHONE",
onHandleEvent: (context) => {
if (context.action === "PASSWORDLESS_CODE_SENT") {
// this event is fired when the user has successfully sent out an OTP email / SMS
} else if (context.action === "PASSWORDLESS_RESTART_FLOW") {
// This event is fired when the user's OTP has expired, or
// they have reached the max limit of number of failed OTP attempts.
} else if (context.action === "SUCCESS" && !context.createdNewSession) {
// this event is fired when successfully completing the OTP email / SMS challenge
// and if it's not used in first factor (cause we do !context.createdNewSession)
}
}
}),
TOTP.init({
onHandleEvent: (context) => {
if (context.action === "TOTP_DEVICE_CREATED") {
// this event is fired during factor setup, when the user has successfully created the TOTP device. They still have to verify it by entering the TOTP.
} else if (context.action === "TOTP_DEVICE_VERIFIED") {
// this event is fired during factor setup, when the user has successfully verified the TOTP device
} else if (context.action === "TOTP_CODE_VERIFIED") {
// this event is fired when the user has successfully verified the TOTP code
// marking the TOTP factor as completed
}
}
}),
MultiFactorAuth.init({
firstFactors: [/*...*/],
onHandleEvent: (context) => {
if (context.action === "FACTOR_CHOOSEN") {
let chosenFactorId = context.factorId;
// this event is fired when the user is shown the screen for
// picking one factor out of a choice of multiple factors
}
}
})
]
})
// 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: [
(window as any).supertokensUIPasswordless.init({
contactMethod: "EMAIL_OR_PHONE",
onHandleEvent: (context) => {
if (context.action === "PASSWORDLESS_CODE_SENT") {
// this event is fired when the user has successfully sent out an OTP email / SMS
} else if (context.action === "PASSWORDLESS_RESTART_FLOW") {
// This event is fired when the user's OTP has expired, or
// they have reached the max limit of number of failed OTP attempts.
} else if (context.action === "SUCCESS" && !context.createdNewSession) {
// this event is fired when successfully completing the OTP email / SMS challenge
// and if it's not used in first factor (cause we do !context.createdNewSession)
}
}
}),
(window as any).supertokensUITOTP.init({
onHandleEvent: (context) => {
if (context.action === "TOTP_DEVICE_CREATED") {
// this event is fired during factor setup, when the user has successfully created the TOTP device. They still have to verify it by entering the TOTP.
} else if (context.action === "TOTP_DEVICE_VERIFIED") {
// this event is fired during factor setup, when the user has successfully verified the TOTP device
} else if (context.action === "TOTP_CODE_VERIFIED") {
// this event is fired when the user has successfully verified the TOTP code
// marking the TOTP factor as completed
}
}
}),
(window as any).supertokensUIMultiFactorAuth.init({
firstFactors: [/*...*/],
onHandleEvent: (context) => {
if (context.action === "FACTOR_CHOOSEN") {
let chosenFactorId = context.factorId;
// this event is fired when the user is shown the screen for
// picking one factor out of a choice of multiple factors
}
}
})
]
})
#
Handling support casesThere are some situations in which users may be locked out of their accounts and would need you to do certain steps to unlock their accounts. These cases are:
#
ERR_CODE_009- This can happen when the second factor is
emailpassword
:- API Path is
/signin POST
. - Output JSON:
{
"status": "SIGN_IN_NOT_ALLOWED",
"reason": "Cannot sign up due to security reasons. Please contact support. (ERR_CODE_009)"
}
- API Path is
- This can happen if the email password account you are trying to do MFA with is not verified.
#
ERR_CODE_010- This can happen when the second factor is
emailpassword
:- API Path is
/signin POST
. - Output JSON:
{
"status": "SIGN_IN_NOT_ALLOWED",
"reason": "Cannot sign up due to security reasons. Please contact support. (ERR_CODE_010)"
}
- API Path is
- This can happen if the email password account you are trying to do MFA with is already linked to another primary user that is not equal to the session user.
#
ERR_CODE_011- This can happen when the second factor is
emailpassword
:- API Path is
/signin POST
. - Output JSON:
{
"status": "SIGN_IN_NOT_ALLOWED",
"reason": "Cannot sign up due to security reasons. Please contact support. (ERR_CODE_011)"
}
- API Path is
- This can happen if the email password account you are trying to do MFA cannot be linked to the session user because there already exists another primary user with the same email.
#
ERR_CODE_012- This can happen when the second factor is
emailpassword
:- API Path is
/signin POST
. - Output JSON:
{
"status": "SIGN_IN_NOT_ALLOWED",
"reason": "Cannot sign up due to security reasons. Please contact support. (ERR_CODE_012)"
}
- API Path is
- In order to link the email password user with the session user, we need to make sure that the session user is a primary user. However, that can fail if there exists another primary user with the same email as the session user, and in this case, this error is returned to the frontend.
#
ERR_CODE_013This can happen when the second factor is
emailpassword
:- API Path is
/signup POST
. - Output JSON:
{
"status": "SIGN_UP_NOT_ALLOWED",
"reason": "Cannot sign up due to security reasons. Please contact support. (ERR_CODE_013)"
}
- API Path is
An example scenario of when in the following scenario:
- A user signs up with their phone number and OTP
- Post sign up, they are asked to add their email and a password for the account. In this case, since the entered email is not verified, this error will be shown.
To resolve this, we recommend that you change the flow to first ask the user to go through the email OTP flow post the first factor sign up, and then add a password to the account. This way, the email will be verified.
#
ERR_CODE_014This can happen when the second factor is
emailpassword
:- API Path is
/signup POST
. - Output JSON:
{
"status": "SIGN_UP_NOT_ALLOWED",
"reason": "Cannot sign up due to security reasons. Please contact support. (ERR_CODE_014)"
}
- API Path is
An example scenario of when in the following scenario:
- Let's say that the app is confgured to not have autmatic account linking during the first factor.
- A user creates an email password account with email
e1
, verifies it, and links social login account to it with emaile2
. - The user logs out, and then creates a social login account with email
e1
. Then, they are asked to add a password to this account. Since an email password account withe1
already exists, SuperTokens will try and link that to this new account, but fail, since the email password account withe1
is already a primary user.
To resolve this, we recommend that manually link the
e1
social login account with thee1
email password account. Or you can enable automatic account linking for first factor and this way, the above scenario will not happen.
#
ERR_CODE_015This can happen when the second factor is
emailpassword
:- API Path is
/signup POST
. - Output JSON:
{
"status": "SIGN_UP_NOT_ALLOWED",
"reason": "Cannot sign up due to security reasons. Please contact support. (ERR_CODE_015)"
}
- API Path is
An example scenario of when in the following scenario:
- A user creates a social login account with email
e1
which becomes a primary user. - The user logs out, and creates another social login account with email
e2
, which also becomes a primary user. - The user is asked to add a password for the new account with an option to also specify an email with it (this is strange, but theoritically possible). They now enter the email
e1
for the email password account. - This will cause this type of error since the linking of the new social logn and email account will fail since there already exists another primary user with the same (
e1
) email.
- A user creates a social login account with email
To resolve this, we recommend not allowing users to specify an email when asking them to add a password for their account.
#
ERR_CODE_016This can happen when the second factor is
emailpassword
:- API Path is
/signup POST
. - Output JSON:
{
"status": "SIGN_UP_NOT_ALLOWED",
"reason": "Cannot sign up due to security reasons. Please contact support. (ERR_CODE_016)"
}
- API Path is
An example scenario of when in the following scenario:
- Let's say that the app is configured to not have automatic account linking during the first factor.
- A user signs up with a social login account using Google with email
e1
, and they add another social account, with Facebook, with the same email. - The user logs out and creates another social login account with email
e1
(say Github), and then tries and adds a password to this account with emaile1
. Here, SuperTokens will try and make the Github login a primary user, but fail, since the emaile1
is already a primary user (with Google login).
To resolve this, we recommend that manually link the
e1
Github social login account with thee1
Google social login account. Or you can enable automatic account linking for first factor and this way, the above scenario will not happen.
#
ERR_CODE_017- This can happen when the second factor is based on the passwordless recipe.
- API Path is
/signinup/code/consume POST
. - Output JSON:
{
"status": "SIGN_IN_UP_NOT_ALLOWED",
"reason": "Cannot sign in / up due to security reasons. Please contact support. (ERR_CODE_017)"
}
- API Path is
- This can happen when the passwordless account is trying to be linked to the account of the first factor, but it can't because the passwordless account is already linked with another primary user.
#
ERR_CODE_018- This can happen when the second factor is based on the passwordless recipe.
- API Path is
/signinup/code/consume POST
. - Output JSON:
{
"status": "SIGN_IN_UP_NOT_ALLOWED",
"reason": "Cannot sign in / up due to security reasons. Please contact support. (ERR_CODE_018)"
}
- API Path is
- This can happen when the passwordless account is trying to be linked to the account of the first factor, but it can't because there exists another primary user with the same email as the passwordless account.
#
ERR_CODE_019- This can happen when the second factor is based on the passwordless recipe.
- API Path is
/signinup/code POST
or/signinup/code/consume POST
. - Output JSON:
{
"status": "SIGN_IN_UP_NOT_ALLOWED",
"reason": "Cannot sign in / up due to security reasons. Please contact support. (ERR_CODE_019)"
}
- API Path is
- This can happen when the passwordless account is trying to be linked to the account of the first factor, but, the first factor account cannot become a primary user because there exists another account with the same email as the first factor user account which is already primary.