Skip to content

Commit

Permalink
feat: implement forgot-password and reset
Browse files Browse the repository at this point in the history
  • Loading branch information
l00pinfinity committed Jan 19, 2023
1 parent 72cd161 commit 75e9464
Show file tree
Hide file tree
Showing 11 changed files with 140 additions and 16 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,10 @@
public class SecurityConfig extends WebSecurityConfigurerAdapter {

@Autowired
UserDetailsService userDetailsService;
private UserDetailsService userDetailsService;

@Autowired
JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;
private JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;

@Bean
public JwtAuthenticationFilter jwtAuthenticationFilter(){
Expand Down
64 changes: 57 additions & 7 deletions src/main/java/com/boitdroid/birdapp/controller/AuthController.java
Original file line number Diff line number Diff line change
@@ -1,39 +1,45 @@
package com.boitdroid.birdapp.controller;

import com.boitdroid.birdapp.exception.AppException;
import com.boitdroid.birdapp.model.user.User;
import com.boitdroid.birdapp.payload.request.ForgotPasswordRequest;
import com.boitdroid.birdapp.payload.request.SignInRequest;
import com.boitdroid.birdapp.payload.request.SignUpRequest;
import com.boitdroid.birdapp.payload.response.ApiResponse;
import com.boitdroid.birdapp.payload.response.JwtAuthenticationResponse;
import com.boitdroid.birdapp.security.JwtTokenProvider;
import com.boitdroid.birdapp.service.UserService;
import com.boitdroid.birdapp.utils.AppUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.support.ServletUriComponentsBuilder;

import javax.validation.Valid;
import java.net.URI;
import java.util.Optional;
import java.util.UUID;

@RestController
@RequestMapping("/api/auth")
public class AuthController {

@Autowired
AuthenticationManager authenticationManager;
private AuthenticationManager authenticationManager;

@Autowired
UserService userService;
private UserService userService;

@Autowired
JwtTokenProvider jwtTokenProvider;
private JwtTokenProvider jwtTokenProvider;

@Autowired
private PasswordEncoder passwordEncoder;

@PostMapping("/signin")
public ResponseEntity<JwtAuthenticationResponse> authenticateUser(@Valid @RequestBody SignInRequest signInRequest) {
Expand All @@ -56,4 +62,48 @@ public ResponseEntity<ApiResponse> registerUser(@Valid @RequestBody SignUpReques

return ResponseEntity.created(location).body(new ApiResponse(Boolean.TRUE, "User registered successfully"));
}

@PostMapping("/forgot-password")
public ResponseEntity<?> forgotPassword(@RequestParam("email") String email){
try{
Optional<User> byEmail = userService.findByEmail(email);

if (byEmail.isEmpty()){
return ResponseEntity.ok(new ApiResponse(Boolean.FALSE,"There is no account with an email address"));
}else{
User user = byEmail.get();
String resetToken = AppUtils.generateToken();
user.setResetToken(resetToken);

//save token
userService.saveUser(user);

//Send user token via Email

return ResponseEntity.ok(new ApiResponse(Boolean.TRUE,"We have sent a password reset token to " + user.getEmail()));
}
}catch (AppException e){
return ResponseEntity.ok(new ApiResponse(Boolean.FALSE,e.getMessage()));
}
}

@PostMapping("/reset")
public ResponseEntity<?> setNewPassword(@RequestParam("token") String token, @RequestBody ForgotPasswordRequest forgotPasswordRequest) {
Optional<User> user = userService.findByResetToken(token);

if (user.isEmpty()) {
return ResponseEntity.ok(new ApiResponse(Boolean.FALSE, "The password reset link is invalid."));
} else {
User users = user.get();

users.setPassword(passwordEncoder.encode(forgotPasswordRequest.getPassword()));
users.setResetToken(null);

userService.saveUser(users);

//Send user password reset confirmation email

return ResponseEntity.ok(new ApiResponse(Boolean.TRUE, "Your password has been successfully reset."));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
@RequestMapping("/api/tweets/{tweetId}/comments")
public class CommentController {
@Autowired
CommentService commentService;
private CommentService commentService;

@GetMapping
public ResponseEntity<PagedResponse<Comment>> getAllComments(@PathVariable(name = "tweetId") Long tweetId,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package com.boitdroid.birdapp.controller;

import com.boitdroid.birdapp.exception.AppException;
import com.boitdroid.birdapp.model.user.User;
import com.boitdroid.birdapp.payload.request.ForgotPasswordRequest;
import com.boitdroid.birdapp.payload.request.InfoRequest;
import com.boitdroid.birdapp.payload.request.UserIdentityAvailability;
import com.boitdroid.birdapp.payload.response.ApiResponse;
Expand All @@ -13,16 +15,19 @@
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.web.bind.annotation.*;

import javax.validation.Valid;
import java.util.Optional;
import java.util.UUID;

@RestController
@RequestMapping("/api/users")
public class UserController {

@Autowired
UserService userService;
private UserService userService;

@GetMapping("/me")
@PreAuthorize("hasRole('ROLE_USER')")
Expand Down Expand Up @@ -102,5 +107,4 @@ public ResponseEntity<UserProfile> setAddress(@CurrentUser UserPrincipal current

return new ResponseEntity(userProfile, HttpStatus.OK);
}

}
4 changes: 4 additions & 0 deletions src/main/java/com/boitdroid/birdapp/model/user/User.java
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,10 @@ public class User extends DateAudit implements Serializable {
@Column(name = "password")
private String password;

@Column(name = "reset_token")
@JsonIgnore
private String resetToken;

@ManyToMany(fetch = FetchType.EAGER)
@JoinTable(name = "user_role", joinColumns = @JoinColumn(name = "user_id", referencedColumnName = "id"), inverseJoinColumns = @JoinColumn(name = "role_id", referencedColumnName = "id"))
private List<Role> roles;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.boitdroid.birdapp.payload.request;

import lombok.Getter;
import lombok.Setter;

import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;

@Getter
@Setter
public class ForgotPasswordRequest {

@NotNull(message = "Password cannot be null")
@NotEmpty(message = "Password cannot be empty")
private String password;

@NotNull(message = "Reset token cannot be null")
@NotEmpty(message = "Reset token cannot be empty")
private String resetToken;
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ public interface UserRepository extends JpaRepository<User,Long> {

Boolean existsByEmail(@NotBlank String email);

Optional<User> findByResetToken(String resetToken);

Optional<User> findByUsernameOrEmail(String username, String email);

default User getUser(UserPrincipal currentUser) {
Expand Down
8 changes: 8 additions & 0 deletions src/main/java/com/boitdroid/birdapp/service/UserService.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
import com.boitdroid.birdapp.payload.response.UserSummary;
import org.springframework.stereotype.Service;

import java.util.Optional;

@Service
public interface UserService {

Expand All @@ -32,4 +34,10 @@ public interface UserService {

UserProfile setOrUpdateInfo(UserPrincipal currentUser, InfoRequest infoRequest);

Optional<User> findByResetToken(String resetToken);

Optional<User> findByEmail(String email);

void saveUser(User user);

}
16 changes: 16 additions & 0 deletions src/main/java/com/boitdroid/birdapp/service/UserServiceImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

@Service
public class UserServiceImpl implements UserService{
Expand Down Expand Up @@ -141,4 +142,19 @@ public UserProfile setOrUpdateInfo(UserPrincipal currentUser, InfoRequest infoRe
}
throw new AccessDeniedException("You don't have permission to update users profile");
}

@Override
public Optional<User> findByResetToken(String resetToken) {
return userRepository.findByResetToken(resetToken);
}

@Override
public Optional<User> findByEmail(String email) {
return userRepository.findByEmail(email);
}

@Override
public void saveUser(User user) {
userRepository.save(user);
}
}
20 changes: 20 additions & 0 deletions src/main/java/com/boitdroid/birdapp/utils/AppUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

import com.boitdroid.birdapp.exception.AppException;

import java.util.Random;

public class AppUtils {

public static void validatePageNumberAndSize(int page, int size) {
Expand All @@ -15,4 +17,22 @@ public static void validatePageNumberAndSize(int page, int size) {
throw new AppException("Page size must not be greater than 10");
}
}

public static String generateToken(){
String upperAlphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
String lowerAlphabet = "abcdefghijklmnopqrstuvwxyz";
String numbers = "0123456789";

String alphaNumeric = upperAlphabet + lowerAlphabet + numbers;
StringBuilder stringBuilder = new StringBuilder();

Random random = new Random();

for(int i = 0; i < 6; i++) {
int index = random.nextInt(alphaNumeric.length());
char randomChar = alphaNumeric.charAt(index);
stringBuilder.append(randomChar);
}
return stringBuilder.toString();
}
}
8 changes: 4 additions & 4 deletions src/main/resources/application.properties
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
spring.datasource.url=jdbc:mysql://localhost:3306/twitter?useSSL=false&serverTimezone=UTC&allowPublicKeyRetrieval=true
spring.datasource.username=root
spring.datasource.password=
spring.datasource.username= root
spring.datasource.password= ZUKMPmfk9Qr9YmM_

server.port =5000
server.port = 5000

spring.jpa.hibernate.ddl-auto=update
spring.jpa.database-platform = org.hibernate.dialect.MySQL5Dialect
Expand All @@ -16,7 +16,7 @@ spring.datasource.initialization-mode=always
cors.allowedOrigins=*

# JWT
app.jwtSecret=secret
app.jwtSecret=t6798y405iuohgrue0iutoh2g4t708934t867yuighj23re6789yuihjb342876yughj345te878yug31yt7849yi3gu1b4ir97f8yuihgkjbqgr798y2u3gjb3q4rew9ft7yigu
app.jwtExpirationInMs=3600000

spring.main.allow-bean-definition-overriding=true
Expand Down

0 comments on commit 75e9464

Please sign in to comment.