Skip to main content
Which UI do you use?
Custom UI
Pre built UI

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("public", 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
await EmailPassword.sendResetPasswordEmail("public", 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.
  • If you only want to create the reset password link and send it yourself, you can use the createResetPasswordLink function instead which will return the link as a string.
Multi Tenancy

For a multi tenant use case, you can pass in a tenantId when creating a user via the signUp function call. This will ensure that the user can only sign into that tenant. In the code above, we pass in the "public" tenantId, which is the default tenantId.

You will also need to pass in the same tenantId to the sendResetPasswordEmail function. This will add the tenantId to the reset link and also ensure that the link has the correct value for the domain based on the tenant ID (assuming that you have overriden the sendEmail function to change the domain in the link).

In case you are using createResetPasswordLink and sending the link yourself, the domain on that link will be the one that's configured in the appInfo object in supertokens.init, so you will need to change the link's domain to the one that belongs to that tenantId.

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 UI do you use?
Custom UI
Pre built UI