Implementing Forgot Password in Spring Boot Project

Here I am going to share how to implement forgotten passwords in Spring Boot projects. I will share a simple implementation where the authenticity of the user is being taken care of by the validity of the email address of the user.

Creating Controllers

In your controller create two @PostMapping routes/endpoints as shown below.

@PostMapping("/forgot_password")
public ResponseMessage processForgotPassword(@RequestBody PasswordResetRequest passwordResetRequest) {
    return passwordPolicyService.updateResetPasswordToken( passwordResetRequest );
}

@PostMapping("/reset_password")
public ResponseMessage processResetPassword(@RequestBody ResetPasswordRequest request) {
    return passwordPolicyService.getByResetPasswordToken( request );
}

In the two @PostMapping I have shown above, I have used my own library (referred to as DTO) to return data from these two methods. The ResponseMessage looks something like this:

package com.bhutanio.myproject.lib;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.Date;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class ResponseMessage {
    private Integer status;
    private String text;
}

In the Java class shown above, I have Lombok annotations. If you are not aware of it, I have shared it in another post.

Another DTO used in this implementation is ResetPasswordRequest.

package com.bhutanio.myproject.lib;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class ResetPasswordRequest {
    private String token;
    private String password;
}

The first method processForgotPassword expects an email address from a client like this:

Forgot password client

processResetPassword expects request from a client like this:

Reset Password client

Creating Service Methods

From the two controller methods, it is vivid that we need two service methods.

Here are my service methods:

public ResponseMessage updateResetPasswordToken(PasswordResetRequest passwordResetRequest)
            throws UserNotFoundException {
    String email = passwordResetRequest.getEmail();
    String token = RandomString.make(30);
    String path = environment.getProperty("endpointUrl");
    try {
        String resetPasswordLink = path + "/reset-password?token=" + token;
        mailSenderService.sendEmail(email, "Forgot password link", resetPasswordLink);
        User user = userRepository.findByEmailAddr(email);
        if (user != null) {
            user.setResetPasswordToken(token);
            userRepository.save(user);
        }
    } catch (UserNotFoundException ex) {
        System.out.println( ex );
        responseMessage.setStatus(SUCCESSFUL_STATUS);
        responseMessage.setText("Please check your email inbox for password reset instructions.");
        return responseMessage;
    }
    responseMessage.setStatus(SUCCESSFUL_STATUS);
    responseMessage.setText("Please check your email inbox for password reset instructions.");
    return responseMessage;
}

public ResponseMessage getByResetPasswordToken(ResetPasswordRequest request) {
    String token = request.getToken();
    String password = request.getPassword();
    User user = userRepository.findByResetPasswordToken(token);
    if (user == null) {
        responseMessage.setStatus(UNSUCCESSFUL_STATUS);
        responseMessage.setText("You've encountered some errors while trying to reset your password.");
        return responseMessage;
    } else {
        updatePassword(user, password);
    }
    responseMessage.setStatus(SUCCESSFUL_STATUS);
    responseMessage.setText("You've successfully reset your password.");
    return responseMessage;
}

These two methods expect the implementation of JavaMailSender in a service named mailSenderService as shown above.

updateResetPasswordToken

Firstly, the user must send an email address via @PostMapping("/forgot_password") using PasswordResetRequest DTO.

String token = RandomString.make(30); This line of code creates an instance of RandomString of 30 characters.

We get the appropriate environment URL and create a link to hit the @PostMapping("/reset_password"). After creating the link, try sending the link to the email address the user has requested.

The random string (token) is being saved in the database so that it can be used to check whether the user is trying with a valid token or hitting with random string trails. This is a very important point to note while implementing this type of forgot password feature.

If you look carefully, you must have seen that the response messages are the same both when email sending succeeds or fails. This is to assure that the user does not try again and again with email addresses that are not present in the database.

getByResetPasswordToken

PasswordResetRequest must contain a valid token and a password string. getByResetPasswordToken method is triggered by the link that the user has received in his/her email.

If the token is valid, the new password is encoded with BCryptPasswordEncoder, password token is reset to null and saved in the database.

updatePassword

This is how updatePassword the method looks like. This method is called if the token the user sent to @PostMapping("/forgot_password") is correct.

public void updatePassword(User user, String newPassword) {
        BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
        String encodedPassword = passwordEncoder.encode(newPassword);
        user.setPassword(encodedPassword);
        user.setResetPasswordToken(null);
        // other implementations if required
        userRepository.save(user);
}

Hope this helps you in making your dream project come true!

Comments

No comments yet. Why don’t you start the discussion?

Leave a Reply

Your email address will not be published. Required fields are marked *