Skip to main content
Which frontend SDK do you use?
supertokens-web-js / mobile
supertokens-auth-react

Changes to email password flow

We start by disabling the public facing sign up API on the backend:

import SuperTokens from "supertokens-node";
import EmailPassword from "supertokens-node/recipe/emailpassword";

SuperTokens.init({
appInfo: {
apiDomain: "...",
appName: "...",
websiteDomain: "..."
},
supertokens: {
connectionURI: "...",
},
recipeList: [
EmailPassword.init({
override: {
apis: (originalImplementation) => {
return {
...originalImplementation,
signUpPOST: undefined,
}
}
}
})
]
});

The next step is to disable the sign up button on the sign in form. This can be done by using CSS to hide the sign up button:

import SuperTokens from "supertokens-auth-react";
import EmailPassword from "supertokens-auth-react/recipe/emailpassword";

SuperTokens.init({
appInfo: {
apiDomain: "...",
appName: "...",
websiteDomain: "..."
},
recipeList: [
EmailPassword.init({
signInAndUpFeature: {
signInForm: {
style: `
[data-supertokens~=headerSubtitle] {
display: none;
}
`,
}
},
}),
]
});

To create email password users, you will need to make an API on your backend which will:

  • Call the signUp function from our backend SDK using that user's email and a fake password. This fake password should be unguessable and should be shared across all invited users.
  • Generate a password reset link and send that as an invite link to the user's email.
  • In the code below, we also make sure that the user who calls this API has the admin role - but you can change this part to whatever you like.

Once the user clicks the link, they will be shown a page asking them to input their password after which, they can login.

import express from "express";
import { verifySession } from "supertokens-node/recipe/session/framework/express";
import { SessionRequest } from "supertokens-node/framework/express";
import UserRoles from "supertokens-node/recipe/userroles";
import EmailPassword from "supertokens-node/recipe/emailpassword";

const FAKE_PASSWORD = "asokdA87fnf30efjoiOI**cwjkn";

let app = express();

app.post("/create-user", verifySession({
overrideGlobalClaimValidators: async function (globalClaimValidators) {
return [...globalClaimValidators,
UserRoles.UserRoleClaim.validators.includes("admin")]
}
}), async (req: SessionRequest, res) => {
let email = req.body.email;

let signUpResult = await EmailPassword.signUp(email, FAKE_PASSWORD);
if (signUpResult.status === "EMAIL_ALREADY_EXISTS_ERROR") {
res.status(400).send("User already exists");
return;
}

// we successfully created the user. Now we should send them their invite link
let passwordResetToken = await EmailPassword.createResetPasswordToken(signUpResult.user.id);

if (passwordResetToken.status === "UNKNOWN_USER_ID_ERROR") {
throw new Error("Should never come here");
}

let inviteLink = "http://localhost:3000/auth/reset-password?token=" + passwordResetToken.token
await EmailPassword.sendEmail({
type: "PASSWORD_RESET",
passwordResetLink: inviteLink,
user: {
email: signUpResult.user.email,
id: signUpResult.user.id
}
});
res.send("Success");
});
  • The code above uses the default password reset path for the invite link (/auth/reset-password). If you are using the pre built UI, this will show password reset page to the user. If you want to show a different UI to the user, then you can use a different path in the link and make your own UI on that path. If you are making your own UI, you can use the password reset functions provided by our frontend SDK to call the password reset token consumption API from the frontend.
  • The sendEmail function used above sends the default password reset email (or the one you customised using the emailDelivery config). Instead, you can also send a different email to the user specifically for the invite flow.
  • You can change the lifetime of the password reset token, and therefore the invite link, by following this guide.

The final step is to:

  • Override the signIn recipe function on the backend to reject sign in attempts which use the fake password. This is done so that if someone knows the fake password, they cannot sign in as the invited user before they reset their password.
  • Override the change password functions to prevent users from changing their password to the fake password.
import SuperTokens from "supertokens-node";
import EmailPassword from "supertokens-node/recipe/emailpassword";

const FAKE_PASSWORD = "asokdA87fnf30efjoiOI**cwjkn"

SuperTokens.init({
appInfo: {
apiDomain: "...",
appName: "...",
websiteDomain: "..."
},
supertokens: {
connectionURI: "...",
},
recipeList: [
EmailPassword.init({
override: {
apis: (originalImplementation) => {
// ... override from previous code snippets...
return originalImplementation
},
functions: (originalImplementation) => {
return {
...originalImplementation,
updateEmailOrPassword: async function (input) {
// This can be called on the backend
// in your own APIs
if (input.password === FAKE_PASSWORD) {
throw new Error("Use a different password")
}
return originalImplementation.updateEmailOrPassword(input);
},
resetPasswordUsingToken: async function (input) {
// This is called during the password reset flow
// when the user enters their new password
if (input.newPassword === FAKE_PASSWORD) {
return {
status: "RESET_PASSWORD_INVALID_TOKEN_ERROR"
}
}
return originalImplementation.resetPasswordUsingToken(input);
},
signIn: async function (input) {
// This is called in the email password sign in API
if (input.password === FAKE_PASSWORD) {
return {
status: "WRONG_CREDENTIALS_ERROR"
}
}
return originalImplementation.signIn(input);
},
}
}
}
})
]
});
Which frontend SDK do you use?
supertokens-web-js / mobile
supertokens-auth-react