Search code examples
javascriptjavareactjsspringspring-boot

Spring boot + React JS Reset password implementation


i'm trying to implement a password reset/forgot password feature in my spring boot/react app but i'm having some problems trying to fully understand it, I'm unsure whether the approach i'm taking is correct or not.

I already have both register/login working

  • I created a REST endpoint: /sendForgotPasswordEmail [POST]
    • This finds a user via their email address they enter in the react form and sees if the account with the email exists.
    • If a user account is found, sends them an email with a unique token with a link to a reset password form (stuck here)
    • I also created a Token entity that generates a random UUID so when the user clicks forgot password and fills in the details, the email generates this link: http://localhost:3000/reset-password/e90c4fa5-2e74-4b44-b3c4-5d6f1c616461
    @PostMapping("/sendForgotPasswordEmail")
    public ResponseEntity<?> sendForgotPasswordEmail(@Valid @RequestBody ForgotPasswordEmailRequest request) {

        User user = userService.findUserByEmailAddress(request.getForgotPasswordEmail());
        ResetPasswordToken resetPasswordToken = userService.createResetPasswordToken(user);

        if(userService.existsByEmailAddress(request.getForgotPasswordEmail())) {
            emailService.sendResetPasswordEmail(request.getForgotPasswordEmail(), resetPasswordToken.getToken());
            return ResponseEntity.ok(new ApiResponse("Please check your email for a password reset link" , true));
        } else {
            return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(null);
        }
    }

  • I created a REST endpoint: /resetPassword [POST]
    • Reads the token generated using @RequestParam to check whether it expired etc.
    • I have a service method called resetPassword(String email, String password) which searches for a user by their email and sets the user password to the provided password from the method.
    @PostMapping("/resetPassword")
    public ResponseEntity<?> resetPassword(@RequestParam("token") String token, @Valid @RequestBody ForgotPasswordEmailRequest request) {
        ResetPasswordToken resetPasswordToken = userService.findByResetPasswordToken(token);

        if(resetPasswordToken == null) throw new BadRequestException("Invalid token");

        Calendar calendar = Calendar.getInstance();

        if((resetPasswordToken.getExpiredAt().getTime() - calendar.getTime().getTime()) <= 0) {
            throw new BadRequestException("Link expired. Generate new link from http://localhost:3000/signup");
        }

        userService.resetPassword(request.getForgotPasswordEmail(), request.getNewPassword());

        return ResponseEntity.ok(token);
    }

UserServiceImpl.java (Methods related to the reset token)


    @Override
    public ResetPasswordToken createResetPasswordToken(User user) {
        ResetPasswordToken resetPasswordToken = new ResetPasswordToken(user);
        return resetPasswordTokenRepository.save(resetPasswordToken);
    }

    @Override
    public ResetPasswordToken findByResetPasswordToken(String token) {
        return resetPasswordTokenRepository.findByToken(token);
    }

    @Override
    public void resetPassword(String emailAddress, String password) {
        User user = findUserByEmailAddress(emailAddress);

        user.setPassword(passwordEncoder.encode(password));

    }


ForgotPasswordEmailRequest.java

public class ForgotPasswordEmailRequest {
    
    private String forgotPasswordEmail;
    
    private String newPassword;

    // constructors / getters and setters

}


The thing i'd like clarity on is how exactly to go about doing this and making sure i can successfully redirect a user to the reset password page, and they can change it from there. I'm unsure where my controller methods are missing something or where i'm going wrong.

When the user reaches the reset password form after being sent an email, what's the process that needs to happen on the front + backend from there.

I'd appreciate any help.


Solution

  • Usually the flow is like this:

    • on frontend side: user clicks 'remind password' where he only provides email address -> that sends POST request with email to backend
    • backend accepts the email, generates token and sends the email with unique url, as you mentioned above,
    • after user checks mail, clicks the link (GET method), you need to have on the frontend side URL with for example GET /password-recovery endpoint, that parses the token from the params, validates in the backend if token is still active and allows to change password on another form. So 2 additional backend requests for token validation and password change.