Forgot Password Feature in Spring Boot Application

In this post, we will show how to implement a forgot password feature for your Spring Boot Application. In my old post, I had shown how to create social login for an application.

Most web applications will have forgot password page and there are different policies about password creation and resetting the password. Overall, you can assume that user will forget a password and will need to reset password.

Flow for Forgot Password

  1. User visits login screen and clicks on forgot password option.
  2. User enters email address in forgot password box.
  3. On Server side, we verify if a user with that email exists or not.
  4. On Server side, we create a time-bound security reset token affiliated with that user and send it in an email, provided that the user exists.
  5. User receives an email to reset password.
  6. Once the user clicks the reset password link which includes the reset token.
  7. User redirects to a page where the user can reset the password.
  8. Then the user submits a new password along with reset token. Based on reset token, we first verify if the user is correct and then saves the new password.
  9. User redirects to login page.

Once now, we have described the flow, we can show how to implement this feature.

Forgot Password UI

A screen where user will enter email address to reset the password, will look like below:

Feature of Forgot Password

Forgot Password

Once the user enters his email address, server side implementation will validate if a user with that email exists or not. In LoginController , this posting of Reset Password will look like below:


        String email = ServletUtil.getAttribute(request, "email");
        User user = userRepository.findUserByEmail(email);

        if(user == null)
        {
            model.addAttribute("error", "We didn't find this user");
            return "forgotpassword";
        }
        PasswordResetToken token = new PasswordResetToken();
        token.setToken(UUID.randomUUID().toString());
        token.setUser(user);
        token.setExpiryDate(30);
        passwordResetTokenRepository.save(token);
        
        Mail mail = new Mail();
        Map<String, Object> modelObj = new HashMap<>();
        modelObj.put("token",token);
        modelObj.put("user", user);
        String url =
                request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort();
        modelObj.put("resetUrl", url + "/resetpassword?token=" + token.getToken());
        mail.setModel(modelObj);
        emailService.sendEmail(mail);

As you see in this code token object is one-to-one mapped with user.  Once the user submits email address, we send him a password reset email with URL.

So this email will look like below:

Once the user clicks on the link from email, user will be redirected to a form to submit new password. When displaying the form, first the reset token will be validated if it has not expired and exists. GET request for reset form will present the form.

POST request will submit the form to reset user password.


    @GetMapping
    public String getPasswordResetPage(@RequestParam(required=false) String token, Model model)
    {
        PasswordResetToken passwordResetToken = passwordResetTokenRepository.findByToken(token);
        if(passwordResetToken == null)
        {
            model.addAttribute("error","Could not find reset token");
        }
        else if(passwordResetToken.isExpired())
        {
            model.addAttribute("error","Reset Token is expired");
        }
        else
        {
            model.addAttribute("token",passwordResetToken.getToken());
        }
        return "resetpassword";
    }

    @PostMapping
    public String handlePasswordReset(HttpServletRequest request, Model model)
    {
        String token = ServletUtil.getAttribute(request, "token");
        PasswordResetToken passwordResetToken = passwordResetTokenRepository.findByToken(token);
        User user = passwordResetToken.getUser();
        String password = ServletUtil.getAttribute(request, "password");
        String confirmPassword = ServletUtil.getAttribute(request, "confirmPassword");
        
        user.setPassword(updatedPassword);
        user.setPasswordConfirm(updatedPassword);
        userRepository.save(user);
        passwordResetTokenRepository.delete(passwordResetToken);

        return "redirect:/login?resetSuccess";

    }

After new password is saved, the reset token is deleted, so it can’t be reused.

Conclusion

In this post, we showed how to implement the user story of forgot password. There are usually different possibilities to reset the password. It mostly depends on what password policies you adapt.

References

  1. Forgot Password Feature – Forgot Password