Create a secure password reset function
Learn how to set up a password reset functionality for your users.
After reading this article, you'll learn how to:
-
Create a 'Forgot password' page
-
Send an email to a user who forgot their password
-
Reset a password properly
Getting started
Before we explore the forgot password functionality, let’s ensure your project is set up with the necessary groundwork. This use case builds directly on the processes described in the Creating the register functionality for webusers article.
We’ll be referencing elements from that setup throughout this guide. While you don’t need a duplicate of the previous application, you do need to have the essentials in place—especially a WebUser model as outlined in the preceding article.
Set up models
1. First, we'll need to add some properties to our WebUser data model to ensure the forgot password functionality works as expected. The properties that are an absolute must-have are as follows:
- 'Password' - Password
- 'Token expiration', format:
[].
- Number - 'Token' - Text (single-line)
2. The next thing we have to do is create the Token model and give the Token model a relation to the WebUser model - many Tokens belong to one Webuser.
The Token model will end up having these properties:
- 'Token expiration' - Number
- 'Token' - Text (single-line)
- 'TokenType' - Text (single-line)
Important! To keep your password reset flow secure, make sure to set the expiration date for your token
Creating the pages
1. If you've followed the article on registering users, you already have a login page present. If you don't have one yet, make sure to create one. We'll first create a new page that'll trigger the forgot password flow.
2. After creating the page, let's add a link to it on our Login page. Navigate to the Login page and add a Text component that links to the internal page '/reset-password-request'.
3. Back to our Reset password request page. Let's create the request by adding a Form component. We'll only need the Email address property.
3. To make the page a bit prettier, change the text of the 'send' button to something more appropriate, like 'Send recovery link'.
4. Next, add a confirmation message beneath the button, similar to the approach used on the login page, but this time configure the button so it redirects users back to the /login page when clicked.
5. Update the default message to reduce confusion. Use clear language such as: ‘An email with password reset instructions has been sent to this address. Please check your inbox to continue.’
Let’s proceed to build out this functionality.
Password reset request action
Open the action connected to the Form you’ve just created. You can access it by navigating to the actions tab and selecting the relevant action, or by clicking the edit button with the Form selected on your page. This is the first of two actions in the password reset process—here, you’ll generate a token and trigger an email to the user who has requested a password reset.
1. Begin by selecting the Start step. The input variable email_address, originating from the Form, should already be present. Add a new object action variable named existing_webuser and configure it to filter by email address.
2. Add a Condition step that checks whether the user exists. If no user is found, you can display an error message.
Caution! Avoid displaying error messages like “User does not exist,” as this could expose which email addresses are registered and create security risks. Instead, use a generic response such as “An email with a recovery link will be sent if we locate the account”
Before you proceed with the next step, make sure you've installed these blocks from Block Store: Generate token, Date/time offset, Delete many records and Send e-mail via SMTP
3. If the user does exist, we can do the following steps:
- Generate a token by using the custom step (function) and name the result random_hex (you can put in any number for the size, we chose 16 for the example).
- Use another custom step called a Date/time offset, which will be used to create our token expiration date. In the block's options, you should enable the Use the current date/time option and set the offset to your desired time (for example, 2 hours)
- To avoid the user using the same token multiple times, we should add a Delete Many Records step right before creating a new token
4. Add a Create record step with the following configuration:
5. The final step in this action is to send the password reset link to the user. Use the Send email step for this purpose. In the email content, include a link to your reset password page and append the generated token to the URL. Here is an example of a password reset email:
More information about configuring this step in Send email via SMTP step article
Reset password action
The second action is the action connected to the reset password form.
1. At the action Start, create 3 input variables: confirm_password, new_password and inputToken, these are the variables that get sent to the action from within the form.
2. Make an action variable, use an object type and then use the WebUser model. Set up the following filter for the action variable: tokens.token equals inputToken
4. Add Date/time offset step where the offset will be one second and set the result to ‘now’
5. Drag in a Condition step and give it the following variable. Use the Token model and call it conditionToken and set up the following filter(s):
- token equals inputToken
- (optional, only if you're using tokens for multiple use cases) token_type equals password reset
6. After setting up the variable, set the 'True' flow in Condition step, so that the action can go through if:
- conditionToken exists
- (optional, only if you're using tokens for multiple use cases) conditionToken.token_type equals PasswordReset
- conditionToken.token_expiration is after or at now
7. Drop another Condition that checks if the new_password and the confirm_password are the same. To do this, you can set up the following rule:
- New_password equals confirm_password
8. Drag in an Update record step to 'Password match flow', add a new object variable with WebUsr model, and use it in the Update record options. At the value mapping, set the Password property to new_password
If you’ve followed along, your action should look like this:
Now, your new secure password reset function should work. Try it out yourself by resetting your password.