Spring Boot Forgot Password Example


This guide will help you to create the Spring Boot REST API for forgot password. The password recovery feature is very important when the application has multiple users. It helps the user to recover/update the password when the user forgot it to access the application again.

Note: In this article, the password is stored in plain text for DEVELOPMENT and TESTING purposes only. The password must be hashed in the PRODUCTION environment. You can use Spring Security Password Storage for hashing the password.

What we’ll build

In this tutorial, we’ll build Spring Boot RESTful service to recover/update the user password. It brings the following features:

1. Validate user by email id.

Optional<User> userOptional = Optional
		.ofNullable(userRepository.findByEmail(email));

if (!userOptional.isPresent()) {
	return "Invalid email id.";
}

2. Generate unique token

private String generateToken() {
	StringBuilder token = new StringBuilder();

	return token.append(UUID.randomUUID().toString())
			.append(UUID.randomUUID().toString()).toString();
}

3. Generate the password recovery URL

String response = userService.forgotPassword(email);

if (!response.startsWith("Invalid")) {
	response = "http://localhost:8080/reset-password?token=" + response;
}

4. Validate the token

Optional<User> userOptional = Optional
		.ofNullable(userRepository.findByToken(token));

if (!userOptional.isPresent()) {
	return "Invalid token.";
}

if (isTokenExpired(tokenCreationDate)) {
	return "Token expired.";
}

5. Update the new password

User user = userOptional.get();

user.setPassword(password);
user.setToken(null);
user.setTokenCreationDate(null);

userRepository.save(user);

return "Your password successfully updated.";

Technologies Used

Find the list of all technologies used in this application.

  1. STS 4
  2. JDK 8
  3. Spring Boot 2.3.0.RELEASE
  4. MySQL database

Dependencies Required

To make it work, make sure these following dependencies are available in your build path. Add the following to your pom.xml.

<dependencies>
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-data-jpa</artifactId>
	</dependency>
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-web</artifactId>
	</dependency>		
	<dependency>
		<groupId>mysql</groupId>
		<artifactId>mysql-connector-java</artifactId>
		<scope>runtime</scope>
	</dependency>
</dependencies>

Project Structure

The final project structure of our application in STS ide will look like as follows:

Spring Boot Forgot Password: Project Structure

MySQL Database Schema

Find the table structure and the insert script of the MySQL database used in this example.

script.sql
create table `user` (
	`id` bigint ,
	`email` varchar ,
	`name` varchar ,
	`password` varchar ,
	`token` varchar ,
	`token_creation_date` timestamp 
); 
insert into `user` (`id`, `email`, `name`, `password`, `token`, `token_creation_date`) values('1','[email protected]','Atul Rai','abcd@123','76cdc01e-f5bb-49dd-ba55-9bcc0e8681c8868f0ce7-00ec-4766-9e6a-067238fc715b','2020-05-23 22:00:04');
insert into `user` (`id`, `email`, `name`, `password`, `token`, `token_creation_date`) values('2','[email protected]','Prince kumar','MyFavCountry@123',NULL,NULL);
insert into `user` (`id`, `email`, `name`, `password`, `token`, `token_creation_date`) values('3','[email protected]','Manish Fartiyal','manish123',NULL,NULL);

application.properties

Configure data source, JPA properties, etc in the application.properties file.

application.properties
# MySQL database connection strings
spring.datasource.url=jdbc:mysql://localhost:3306/spring_dev
spring.datasource.username=root
spring.datasource.password=root

# JPA property settings
spring.jpa.hibernate.ddl-auto=update
spring.jpa.properties.hibernate.show_sql=true

User Entity

The User entity class will create the table in MySQL database as shown in the above script. It will be used with the UserRepository interface later to perform the database operation using Spring Data JPA.

User.java
package org.websparrow.entity;

import java.time.LocalDateTime;

import javax.persistence.*;

@Entity
public class User {

	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	private Long id;
	private String name;
	@Column(unique = true)
	private String email;
	private String password;
	private String token;
	@Column(columnDefinition = "TIMESTAMP")
	private LocalDateTime tokenCreationDate;

	// Generate Getters and Setters...
}

User Repository

UserRepository interface extends the JpaRepository interface of the package org.springframework.data.jpa.repository.

UserRepository.java
package org.websparrow.repository;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import org.websparrow.entity.User;

@Repository
public interface UserRepository extends JpaRepository<User, Long> {

	User findByEmail(String email);

	User findByToken(String token);
}

User Service

The UserService class communicates with the database and validates the user information like email id, token & token expiry. If all the validation successfully passed, it will update the new user password and respond accordingly.

UserService.java
package org.websparrow.service;

import java.time.*;
import java.util.*;

import org.springframework.*;
import org.websparrow.entity.User;
import org.websparrow.repository.UserRepository;

@Service
public class UserService {

	private static final long EXPIRE_TOKEN_AFTER_MINUTES = 30;

	@Autowired
	private UserRepository userRepository;

	public String forgotPassword(String email) {

		Optional<User> userOptional = Optional
				.ofNullable(userRepository.findByEmail(email));

		if (!userOptional.isPresent()) {
			return "Invalid email id.";
		}

		User user = userOptional.get();
		user.setToken(generateToken());
		user.setTokenCreationDate(LocalDateTime.now());

		user = userRepository.save(user);

		return user.getToken();
	}

	public String resetPassword(String token, String password) {

		Optional<User> userOptional = Optional
				.ofNullable(userRepository.findByToken(token));

		if (!userOptional.isPresent()) {
			return "Invalid token.";
		}

		LocalDateTime tokenCreationDate = userOptional.get().getTokenCreationDate();

		if (isTokenExpired(tokenCreationDate)) {
			return "Token expired.";

		}

		User user = userOptional.get();

		user.setPassword(password);
		user.setToken(null);
		user.setTokenCreationDate(null);

		userRepository.save(user);

		return "Your password successfully updated.";
	}

	/**
	 * Generate unique token. You may add multiple parameters to create a strong
	 * token.
	 * 
	 * @return unique token
	 */
	private String generateToken() {
		StringBuilder token = new StringBuilder();

		return token.append(UUID.randomUUID().toString())
				.append(UUID.randomUUID().toString()).toString();
	}

	/**
	 * Check whether the created token expired or not.
	 * 
	 * @param tokenCreationDate
	 * @return true or false
	 */
	private boolean isTokenExpired(final LocalDateTime tokenCreationDate) {

		LocalDateTime now = LocalDateTime.now();
		Duration diff = Duration.between(tokenCreationDate, now);

		return diff.toMinutes() >= EXPIRE_TOKEN_AFTER_MINUTES;
	}
}

User Controller

UserController class handles the user request and responds accordingly.

UserController.java
package org.websparrow.controller;

import org.springframework.*;
import org.websparrow.service.UserService;

@RestController
public class UserController {

	@Autowired
	private UserService userService;

	@PostMapping("/forgot-password")
	public String forgotPassword(@RequestParam String email) {

		String response = userService.forgotPassword(email);

		if (!response.startsWith("Invalid")) {
			response = "http://localhost:8080/reset-password?token=" + response;
		}
		return response;
	}

	@PutMapping("/reset-password")
	public String resetPassword(@RequestParam String token,
			@RequestParam String password) {

		return userService.resetPassword(token, password);
	}
}

Execute it

SpringBootPasswordResetApp.java
package org.websparrow;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class SpringBootPasswordResetApp {

	public static void main(String[] args) {
		SpringApplication.run(SpringBootPasswordResetApp.class, args);
	}
}

Test it

Let’s test the application.  To test the application open the Postman and follow the below steps:

Step 1: Hit this below endpoint in your Postman client by providing your email id:

Endpoint: http://localhost:8080/forgot-password

HTTP method: POST

Response:

1. Invalid email id: when you enter invalid email id.

2. Password reset endpoint: if the email is valid.

Spring Boot Forgot Password

Step 2: Use the provided endpoint with your new password:

Endpoint: http://localhost:8080/reset-password?token=<some-unique-token>

HTTP method: PUT

Response:

1. Invalid token: if the token is not valid.

2. Token expired: if the token is expired (Expire threshold value is 30 minutes)

3. Your password successfully updated: when all validation is passed.

Spring Boot Reset Password

References

  1. Spring Data CrudRepository Interface Example
  2. Spring Data JPA Derived Query Methods Example
  3. Spring Boot RESTful CRUD Example with MySQL Database

Similar Posts

About the Author

Atul Rai
I love sharing my experiments and ideas with everyone by writing articles on the latest technological trends. Read all published posts by Atul Rai.