Implement password Reset feature for your java application

Grettings!

In this article I’ll show you how you can implement a password reset feature in your java application.
You have to follow this {var} simple steps to implement this feature.

1. Create a model for validation token

You can create a parent class ValidationToken that you can extend in any specific type of validation token class.
Extend it to AcValidationToken class that you are going to use for this purpose.
@MappedSuperclass 
public class ValidationToken extends BaseEntity {    
    private String token; 
    private boolean tokenValid; 
    // getters and setters 
}
@Entity
public class AcValidationToken extends ValidationToken{
    @OneToOne   
     private User user;    
     private String reason;
    // getters and setters
}
Here we’ve created a @OneToOne relationship with user because we are going to reset password for that user.

2. Repository and Service for that model

AcValidationTokenRepository.java
@Repository
public interface AcValidationTokenRepository extends JpaRepository<AcValidationToken, Long> {
    AcValidationToken findByToken(String token);
}
AcValidationTokenServiceImpl.java
@Service
public class AcValidationTokenServiceImpl implements AcValidationTokenService {
    private final AcValidationTokenRepository tokenRepo;
    @Autowired    
    public AcValidationTokenServiceImpl(AcValidationTokenRepository tokenRepo) {
            this.tokenRepo = tokenRepo;    
    }

    @Override    
    public AcValidationToken save(AcValidationToken acValidationToken) {
            return this.tokenRepo.save(acValidationToken);   
     }

    @Override    
    public AcValidationToken findOne(Long id) {
            return this.tokenRepo.findOne(id);    
    }

    @Override   
    public AcValidationToken findByToken(String token) {
            if (token == null) return null;        
            return this.tokenRepo.findByToken(token);    
    }

    @Override   
     public void delete(Long id) {
            this.tokenRepo.delete(id);    
    }

    @Override    
    public boolean isTokenValid(String token) {
        if (token == null || token.isEmpty()) return false;
        AcValidationToken acValidationToken = this.findByToken(token);
        return acValidationToken != null && acValidationToken.isTokenValid();    
    }
}

3. Create a token generator utility class

We’ll use this class to generate a unique token.
public class SessionIdentifierGenerator {
    private SecureRandom random = new SecureRandom();

    public String nextSessionId() {
            return new BigInteger(130, random).toString(32);    
    }

    public String nextPassword(){
        return new BigInteger(130, random).toString(32);
    }

}

4. Create Service Methods

So let’s create two service methods for this process. First one is responsible for generating validation token and email it to the user.
This method will take and email where it’ll send the token and validationUrl to let user click that link to verify their email. if validationUrl is null then it’ll just send the token. (useful when you are prompting user to enter the token to verify after sending the email)
@Override
public void requireAccountValidationByEmail(String email, String validationUrl) throws UserNotFoundException {
    if (email == null) throw new IllegalArgumentException("Email invalid!");    
    User user = this.findByEmail(email); 
    SessionIdentifierGenerator sessionIdentifierGenerator = new SessionIdentifierGenerator();    
    AcValidationToken acValidationToken = new AcValidationToken();    
    acValidationToken.setToken(sessionIdentifierGenerator.nextSessionId());    
    acValidationToken.setTokenValid(true);    
    acValidationToken.setUser(user);    // save acvalidationtoken    
    acValidationToken = this.acValidationTokenService.save(acValidationToken);    
    if (validationUrl == null) {
            this.mailService.sendEmail(user.getEmail(), "Verification token", "Your verification token is: " + acValidationToken.getToken());
        return;    
    }
    // build confirmation link    
    String confirmationLink = baseUrlApi.trim() + validationUrl + "?token=" + acValidationToken.getToken() + "&enabled=true";    
    // send link by email    
    this.mailService.sendEmail(user.getEmail(), "Please verify you account", "Please verify your email by clicking this link " + confirmationLink);
}
This method will be used to reset users password. It takes two arguments. token  and password (new password). This method will check if token is valid and reset password if it’s vaid.
@Override
@Transactional
public User resetPassword(String token, String password) throws NullPasswordException, UserAlreadyExistsException, UserInvalidException {
    if (password.length() < 6)
        throw new IllegalArgumentException("Password length should be at least 6");    AcValidationToken 
    acValidationToken = this.acValidationTokenService.findByToken(token);    
    if(!acValidationTokenService.isTokenValid(token)) throw new UserInvalidException("Token invalid");    
    
    User user = acValidationToken.getUser();
    user.setPassword(PasswordUtil.encryptPassword(password, PasswordUtil.EncType.BCRYPT_ENCODER, null));
     acValidationToken.setTokenValid(false); 
    acValidationToken.setReason("Password Reset");
    user = this.save(user);
    acValidationToken.setUser(user);
    this.acValidationTokenService.save(acValidationToken);
    return user;
}
Okay now, in our controller we have to create these four routes.
Send a password reset email
// Password reset
@PostMapping("/resetPassword/verifyEmail")
private ResponseEntity verifyEmail(@RequestParam("email") String email) throws UserNotFoundException {
    this.userService.requireAccountValidationByEmail(email, null);   
     return ResponseEntity.status(HttpStatus.OK).build();
}
So if you send a request to this endpoint it’ll send you an email with a verification token.
Check Token Validity
@PostMapping("/checkTokenValidity")
private ResponseEntity checkTokenValidity(@RequestParam(value = "token") String token) {
    if (!this.acValidationTokenService.isTokenValid(token))
        return ResponseEntity.status(HttpStatus.FORBIDDEN).build();    
    return ResponseEntity.ok(token);
}
this endpoint is for checking the token validity. Remember to send a request to this method before reseting password. But don’t worry, we have extra protection in out resetPassword() servicemethod.  It’ll check again if this token is valid or not. And will throw an exception if invalid.
Now reset the password
@PostMapping("/resetPassword")
private ResponseEntity resetPassword(@RequestParam("token") String token,  @RequestParam("password") String password) throws Exception, NullPasswordException, UserAlreadyExistsException, UserInvalidException {
if (!this.acValidationTokenService.isTokenValid(token))
    return ResponseEntity.status(HttpStatus.FORBIDDEN).build(); 
    User user = this.userService.resetPassword(token, password);
    return ResponseEntity.ok(user);
}
Wholla!! you are up!

Since this process needs to send emails, so if you want you can take a look at this article about how you can send email from spring application.

Please let me know if you have any suggestions or improvement ideas during that process. I’m happy to learn new things and ideas.

1 thought on “Implement password Reset feature for your java application

Leave a Reply

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