From 664fb2d65162b2ea65efdae1f0211a814efbb253 Mon Sep 17 00:00:00 2001 From: Carlos Gutierrez Date: Sun, 18 Jan 2026 18:48:26 -0500 Subject: [PATCH] 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. --- .../api/main/controllers/UserController.java | 26 +++++++++++++------ src/main/java/com/api/main/entity/Token.java | 4 +++ .../main/repositories/TokenRepository.java | 13 ++++++++-- .../com/api/main/services/AuthService.java | 26 +++++++++++++++++-- 4 files changed, 57 insertions(+), 12 deletions(-) diff --git a/src/main/java/com/api/main/controllers/UserController.java b/src/main/java/com/api/main/controllers/UserController.java index faf3fbe..ad49fbd 100644 --- a/src/main/java/com/api/main/controllers/UserController.java +++ b/src/main/java/com/api/main/controllers/UserController.java @@ -6,7 +6,6 @@ import com.api.main.dto.ErrorResponse; import com.api.main.dto.UserResponse; import com.api.main.entity.User; import com.api.main.services.AuthService; -import jakarta.servlet.http.HttpServletRequest; import jakarta.validation.Valid; import java.util.Collections; import java.util.Map; @@ -53,11 +52,22 @@ public class UserController { @PreAuthorize("hasRole('ADMIN')") @PostMapping("/create") - public ResponseEntity createUser(@Valid @RequestBody CreateUserRequest request) { + public ResponseEntity createUser( + @Valid @RequestBody CreateUserRequest request, Authentication authentication) { 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 = authService.registerUser( - request.getUsername(), request.getEmail(), request.getPassword(), request.getRole()); + request.getUsername(), + request.getEmail(), + request.getPassword(), + request.getRole(), + username); UserResponse response = new UserResponse( user.getId(), @@ -71,12 +81,12 @@ public class UserController { } @PostMapping("/logout") - public ResponseEntity logout(HttpServletRequest request) { + public ResponseEntity logout() { try { - String authHeader = request.getHeader("Authorization"); - if (authHeader != null && authHeader.startsWith("Bearer ")) { - String token = authHeader.substring(7); - authService.logout(token); + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + if (authentication != null && authentication.isAuthenticated()) { + String username = authentication.getName(); + authService.logoutByUsername(username); } return ResponseEntity.ok( Map.of( diff --git a/src/main/java/com/api/main/entity/Token.java b/src/main/java/com/api/main/entity/Token.java index d0342d5..1991a3a 100644 --- a/src/main/java/com/api/main/entity/Token.java +++ b/src/main/java/com/api/main/entity/Token.java @@ -155,4 +155,8 @@ public class Token { public void setRevoked(boolean revoked) { this.revoked = revoked; } + + public Object getRevoked() { + return this.revoked; + } } diff --git a/src/main/java/com/api/main/repositories/TokenRepository.java b/src/main/java/com/api/main/repositories/TokenRepository.java index 5dd229e..10da587 100644 --- a/src/main/java/com/api/main/repositories/TokenRepository.java +++ b/src/main/java/com/api/main/repositories/TokenRepository.java @@ -6,6 +6,7 @@ import java.util.Optional; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; import org.springframework.stereotype.Repository; /* @@ -39,14 +40,22 @@ public interface TokenRepository extends JpaRepository { */ Optional 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 findTopByUsernameOrderByExpiresAtDesc(String username); + /* * Revoke all tokens associated with a specific username. * @param username The username whose tokens are to be revoked * */ - @Modifying + @Modifying(clearAutomatically = true, flushAutomatically = true) @Query(Constants.ADDING_TOKEN_QUERY) - void revokeAllUserTokens(String username); + void revokeAllUserTokens(@Param("username") String username); /* * Delete all expired tokens from the database. diff --git a/src/main/java/com/api/main/services/AuthService.java b/src/main/java/com/api/main/services/AuthService.java index 0c7c6b0..6aace6e 100644 --- a/src/main/java/com/api/main/services/AuthService.java +++ b/src/main/java/com/api/main/services/AuthService.java @@ -51,6 +51,13 @@ public class AuthService { this.authenticationManager = authenticationManager; } + public boolean isTokenRevokedForUser(String username) { + return tokenRepository + .findTopByUsernameOrderByExpiresAtDesc(username) + .map(Token::isRevoked) + .orElse(true); + } + @Transactional public LoginResponse authenticate(LoginRequest request) { try { @@ -78,7 +85,9 @@ public class AuthService { private String generateToken() { byte[] randomBytes = new byte[32]; 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) { @@ -87,6 +96,10 @@ public class AuthService { .findByUsername(username) .orElseThrow(() -> new RuntimeException("User not found")); + if (isTokenRevokedForUser(username)) { + throw new RuntimeException("Token is revoked"); + } + return new UserResponse( user.getId(), user.getUsername(), @@ -95,7 +108,11 @@ public class AuthService { } @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)) { 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) { return tokenRepository.findByTokenAndRevokedFalse(token).isPresent(); }