fix: fixing the token expiration issue in AuthService
Adding a fix to validate the token on api calls to ensure that expired tokens are not accepted.
This commit is contained in:
@@ -6,7 +6,6 @@ import com.api.main.dto.ErrorResponse;
|
|||||||
import com.api.main.dto.UserResponse;
|
import com.api.main.dto.UserResponse;
|
||||||
import com.api.main.entity.User;
|
import com.api.main.entity.User;
|
||||||
import com.api.main.services.AuthService;
|
import com.api.main.services.AuthService;
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
|
||||||
import jakarta.validation.Valid;
|
import jakarta.validation.Valid;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@@ -53,11 +52,22 @@ public class UserController {
|
|||||||
|
|
||||||
@PreAuthorize("hasRole('ADMIN')")
|
@PreAuthorize("hasRole('ADMIN')")
|
||||||
@PostMapping("/create")
|
@PostMapping("/create")
|
||||||
public ResponseEntity<?> createUser(@Valid @RequestBody CreateUserRequest request) {
|
public ResponseEntity<?> createUser(
|
||||||
|
@Valid @RequestBody CreateUserRequest request, Authentication authentication) {
|
||||||
try {
|
try {
|
||||||
|
Authentication authenticationContext = SecurityContextHolder.getContext().getAuthentication();
|
||||||
|
if (authenticationContext == null && !authenticationContext.isAuthenticated()) {
|
||||||
|
return ResponseEntity.status(401)
|
||||||
|
.body(new ErrorResponse(Constants.ERROR, Constants.UNAUTHORIZED_MESSAGE));
|
||||||
|
}
|
||||||
|
String username = authentication.getName();
|
||||||
User user =
|
User user =
|
||||||
authService.registerUser(
|
authService.registerUser(
|
||||||
request.getUsername(), request.getEmail(), request.getPassword(), request.getRole());
|
request.getUsername(),
|
||||||
|
request.getEmail(),
|
||||||
|
request.getPassword(),
|
||||||
|
request.getRole(),
|
||||||
|
username);
|
||||||
UserResponse response =
|
UserResponse response =
|
||||||
new UserResponse(
|
new UserResponse(
|
||||||
user.getId(),
|
user.getId(),
|
||||||
@@ -71,12 +81,12 @@ public class UserController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/logout")
|
@PostMapping("/logout")
|
||||||
public ResponseEntity<?> logout(HttpServletRequest request) {
|
public ResponseEntity<?> logout() {
|
||||||
try {
|
try {
|
||||||
String authHeader = request.getHeader("Authorization");
|
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
|
||||||
if (authHeader != null && authHeader.startsWith("Bearer ")) {
|
if (authentication != null && authentication.isAuthenticated()) {
|
||||||
String token = authHeader.substring(7);
|
String username = authentication.getName();
|
||||||
authService.logout(token);
|
authService.logoutByUsername(username);
|
||||||
}
|
}
|
||||||
return ResponseEntity.ok(
|
return ResponseEntity.ok(
|
||||||
Map.of(
|
Map.of(
|
||||||
|
|||||||
@@ -155,4 +155,8 @@ public class Token {
|
|||||||
public void setRevoked(boolean revoked) {
|
public void setRevoked(boolean revoked) {
|
||||||
this.revoked = revoked;
|
this.revoked = revoked;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Object getRevoked() {
|
||||||
|
return this.revoked;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import java.util.Optional;
|
|||||||
import org.springframework.data.jpa.repository.JpaRepository;
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
import org.springframework.data.jpa.repository.Modifying;
|
import org.springframework.data.jpa.repository.Modifying;
|
||||||
import org.springframework.data.jpa.repository.Query;
|
import org.springframework.data.jpa.repository.Query;
|
||||||
|
import org.springframework.data.repository.query.Param;
|
||||||
import org.springframework.stereotype.Repository;
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -39,14 +40,22 @@ public interface TokenRepository extends JpaRepository<Token, Long> {
|
|||||||
*/
|
*/
|
||||||
Optional<Token> findByTokenAndRevokedFalse(String token);
|
Optional<Token> findByTokenAndRevokedFalse(String token);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Find the most recent token for a given username.
|
||||||
|
* @param username The username associated with the token
|
||||||
|
* @return Optional containing the most recent Token if found, else empty
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
Optional<Token> findTopByUsernameOrderByExpiresAtDesc(String username);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Revoke all tokens associated with a specific username.
|
* Revoke all tokens associated with a specific username.
|
||||||
* @param username The username whose tokens are to be revoked
|
* @param username The username whose tokens are to be revoked
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
@Modifying
|
@Modifying(clearAutomatically = true, flushAutomatically = true)
|
||||||
@Query(Constants.ADDING_TOKEN_QUERY)
|
@Query(Constants.ADDING_TOKEN_QUERY)
|
||||||
void revokeAllUserTokens(String username);
|
void revokeAllUserTokens(@Param("username") String username);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Delete all expired tokens from the database.
|
* Delete all expired tokens from the database.
|
||||||
|
|||||||
@@ -51,6 +51,13 @@ public class AuthService {
|
|||||||
this.authenticationManager = authenticationManager;
|
this.authenticationManager = authenticationManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isTokenRevokedForUser(String username) {
|
||||||
|
return tokenRepository
|
||||||
|
.findTopByUsernameOrderByExpiresAtDesc(username)
|
||||||
|
.map(Token::isRevoked)
|
||||||
|
.orElse(true);
|
||||||
|
}
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
public LoginResponse authenticate(LoginRequest request) {
|
public LoginResponse authenticate(LoginRequest request) {
|
||||||
try {
|
try {
|
||||||
@@ -78,7 +85,9 @@ public class AuthService {
|
|||||||
private String generateToken() {
|
private String generateToken() {
|
||||||
byte[] randomBytes = new byte[32];
|
byte[] randomBytes = new byte[32];
|
||||||
new java.security.SecureRandom().nextBytes(randomBytes);
|
new java.security.SecureRandom().nextBytes(randomBytes);
|
||||||
return UUID.randomUUID().toString() + "-" + Base64.getUrlEncoder().withoutPadding().encodeToString(randomBytes);
|
return UUID.randomUUID().toString()
|
||||||
|
+ "-"
|
||||||
|
+ Base64.getUrlEncoder().withoutPadding().encodeToString(randomBytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
public UserResponse getCurrentUser(String username) {
|
public UserResponse getCurrentUser(String username) {
|
||||||
@@ -87,6 +96,10 @@ public class AuthService {
|
|||||||
.findByUsername(username)
|
.findByUsername(username)
|
||||||
.orElseThrow(() -> new RuntimeException("User not found"));
|
.orElseThrow(() -> new RuntimeException("User not found"));
|
||||||
|
|
||||||
|
if (isTokenRevokedForUser(username)) {
|
||||||
|
throw new RuntimeException("Token is revoked");
|
||||||
|
}
|
||||||
|
|
||||||
return new UserResponse(
|
return new UserResponse(
|
||||||
user.getId(),
|
user.getId(),
|
||||||
user.getUsername(),
|
user.getUsername(),
|
||||||
@@ -95,7 +108,11 @@ public class AuthService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
public User registerUser(String username, String email, String password, String role) {
|
public User registerUser(
|
||||||
|
String username, String email, String password, String role, String createdBy) {
|
||||||
|
if (isTokenRevokedForUser(createdBy)) {
|
||||||
|
throw new RuntimeException("Token is revoked");
|
||||||
|
}
|
||||||
if (userRepository.existsByUsername(username)) {
|
if (userRepository.existsByUsername(username)) {
|
||||||
throw new RuntimeException("Username already exists");
|
throw new RuntimeException("Username already exists");
|
||||||
}
|
}
|
||||||
@@ -124,6 +141,11 @@ public class AuthService {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
public void logoutByUsername(String username) {
|
||||||
|
tokenRepository.revokeAllUserTokens(username);
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isTokenValid(String token) {
|
public boolean isTokenValid(String token) {
|
||||||
return tokenRepository.findByTokenAndRevokedFalse(token).isPresent();
|
return tokenRepository.findByTokenAndRevokedFalse(token).isPresent();
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user