Skip to main content

Forgot Password flow

There are two steps in the forgot password flow:

  • Sending the password reset email to a user's email ID
  • Once the user clicks the link, asking them for and updating their password.

Step 1: Sending the password reset email#

You should create a new screen on your website that asks the user to enter their email to which an email will be sent. This screen should ideally be linked to from the sign in form.

Once the user has enters their email, you can use the following function to send a reset password email to that user:

import { sendPasswordResetEmail } from "supertokens-web-js/recipe/emailpassword";

async function sendEmailClicked(email: string) {
try {
let response = await sendPasswordResetEmail({
formFields: [{
id: "email",
value: email
}]
});

if (response.status === "FIELD_ERROR") {
// one of the input formFields failed validaiton
response.formFields.forEach(formField => {
if (formField.id === "email") {
// Email validation failed (for example incorrect email syntax).
window.alert(formField.error)
}
})
} else {
// reset password email sent.
window.alert("Please check your email for the password reset link")
}
} 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.");
}
}
}
important

If the input email ID does not belong to a user who signed up previously, SuperTokens will not send them an email. However, the frontend will still receive an "OK" status back.

Changing the password reset link#

By default, the password reset link will point to the websiteDomain that is configured on the backend, on the /auth/reset-password route (where /auth is the default value of websiteBasePath).

If you want to change this to a different path, a different domain, or deep link it to your mobile / desktop app, then you can do so on the backend in the following way:

import SuperTokens from "supertokens-node";
import EmailPassword from "supertokens-node/recipe/emailpassword";
import { SMTPService } from "supertokens-node/recipe/emailpassword/emaildelivery";

SuperTokens.init({
supertokens: {
connectionURI: "...",
},
appInfo: {
apiDomain: "...",
appName: "...",
websiteDomain: "..."
},
recipeList: [
EmailPassword.init({
emailDelivery: {
override: (originalImplementation) => {
return {
...originalImplementation,
sendEmail: async function (input) {
if (input.type === "PASSWORD_RESET") {
// You can change the path, domain of the reset password link,
// or even deep link it to your mobile app
return originalImplementation.sendEmail({
...input,
passwordResetLink: input.passwordResetLink.replace(
// This is: `${websiteDomain}${websiteBasePath}/reset-password`
"http://localhost:3000/auth/reset-password",
"http://localhost:3000/your/path"
)
})
}
return originalImplementation.sendEmail(input);
}
}
}
}
})
]
});
Multi Tenancy

For a multi tenant setup, the input to the sendEmail function will also contain the tenantId. You can use this to determine the correct value to set for the websiteDomain in the generated link.

Step 2: Updating the user's password#

The default password reset link is of the form ${websiteDomain}/auth/reset-password?token=... where /auth is the default value of websiteBasePath that is configured on the backend.

Once the user clicks on the reset password link you need to ask them to enter their new password and call the function as shown below to change their password.

import { submitNewPassword } from "supertokens-web-js/recipe/emailpassword";

async function newPasswordEntered(newPassword: string) {
try {
let response = await submitNewPassword({
formFields: [{
id: "password",
value: newPassword
}]
});

if (response.status === "FIELD_ERROR") {
response.formFields.forEach(formField => {
if (formField.id === "password") {
// New password did not meet password criteria on the backend.
window.alert(formField.error)
}
})
} else if (response.status === "RESET_PASSWORD_INVALID_TOKEN_ERROR") {
// the password reset token in the URL is invalid, expired, or already consumed
window.alert("Password reset failed. Please try again")
window.location.assign("/auth") // back to the login scree.
} else {
window.alert("Password reset successful!")
window.location.assign("/auth")
}
} 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.");
}
}
}

See also#