Skip to main content

This is the legacy method of implementing MFA. It has several disadvantages compared to using our MFA recipe.

Setting up the 1st factor

1) Initialisation#

Start by following the recipe guide for the first factor. In this guide, we will take the example of thirdpartyemailpassword recipe as being the first factor.

After following the backend quick setup section (or any of the framework specific integration guides), you should have all the auth APIs exposed to the frontend via the SuperTokens middleware. The supertokens.init code on the server would look like this:

import supertokens from "supertokens-node";
import Session from "supertokens-node/recipe/session";
import UserMetadata from "supertokens-node/recipe/usermetadata";
import ThirdPartyEmailPassword from"supertokens-node/recipe/thirdpartyemailpassword";

framework: "express",
supertokens: {
connectionURI: "",
apiKey: "",
appInfo: {
// learn more about this on
appName: "<YOUR_APP_NAME>",
apiDomain: "<YOUR_API_DOMAIN>",
websiteDomain: "<YOUR_WEBSITE_DOMAIN>",
apiBasePath: "/auth",
websiteBasePath: "/auth"
recipeList: [
Session.init(), // initializes session features
UserMetadata.init() // initializes the user metadata feature

You should have also added the SuperTokens middleware and errorHandler (depending on your framework) to your application. We are not showing it in the above code snippet for brevity, but it is explained in the ThirdPartyEmailPassword recipe guide.

2) Adding SecondFactorClaim#

After sign up or sign in of the first factor, the existence of the session signifies the completion of the first factor, but we want to explicitly mark the second factor as incomplete. This can be done by overriding the createNewSession function in the Session.init config:

import Session from "supertokens-node/recipe/session";
import { BooleanClaim } from "supertokens-node/recipe/session/claims";

This will be used to modify the session's access token payload
to add {"2fa-completed": false} into it.
export const SecondFactorClaim = new BooleanClaim({
fetchValue: () => false,
key: "2fa-completed",

override: {
functions: (originalImplementation) => {
return {
/* This function is called after signing in or signing up via the first factor */
createNewSession: async function (input) {
return originalImplementation.createNewSession({
accessTokenPayload: {
...(await, input.recipeUserId, input.tenantId, undefined, input.userContext)),

We add SecondFactorClaim into the access token payload. This will be set to false on session creation (see fetchValue in the claim definition).

Looking for older versions of the documentation?
Which UI do you use?
Custom UI
Pre built UI