Skip to content

Commit 9c8828b

Browse files
refactor(auth): simplify AuthController and remove unused code
1 parent 603a2d2 commit 9c8828b

File tree

10 files changed

+168
-808
lines changed

10 files changed

+168
-808
lines changed

src/main/java/com/otavio/aifoodapp/controller/AuthController.java

Lines changed: 29 additions & 201 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,9 @@
33
import java.util.HashMap;
44
import java.util.Map;
55
import java.util.Optional;
6-
import java.util.concurrent.ConcurrentHashMap;
76

8-
import org.springframework.http.HttpStatus;
97
import org.springframework.http.ResponseEntity;
108
import org.springframework.security.core.Authentication;
11-
import org.springframework.security.core.context.SecurityContextHolder;
12-
import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;
139
import org.springframework.security.oauth2.core.user.OAuth2User;
1410
import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler;
1511
import org.springframework.web.bind.annotation.GetMapping;
@@ -20,42 +16,28 @@
2016
import com.otavio.aifoodapp.dto.UserDTO;
2117
import com.otavio.aifoodapp.model.User;
2218
import com.otavio.aifoodapp.repository.UserRepository;
23-
import com.otavio.aifoodapp.security.TokenService;
24-
import com.otavio.aifoodapp.service.FoodItemService;
2519

26-
import jakarta.servlet.http.Cookie;
2720
import jakarta.servlet.http.HttpServletRequest;
2821
import jakarta.servlet.http.HttpServletResponse;
29-
import jakarta.servlet.http.HttpSession;
3022
import lombok.extern.slf4j.Slf4j;
3123

3224
/**
33-
* Controlador consolidado para autenticação
34-
* Combina funcionalidades de status, login, logout e gerenciamento de tokens
25+
* Standard OAuth2 Authentication Controller
26+
* Uses Spring Security OAuth2 without custom token handling
3527
*/
3628
@RestController
3729
@RequestMapping("/api/auth")
3830
@Slf4j
3931
public class AuthController {
4032

41-
private final FoodItemService foodItemService;
42-
private final TokenService tokenService;
4333
private final UserRepository userRepository;
4434

45-
// Cache para controlar a frequência de verificações por sessão
46-
private final Map<String, Long> lastStatusChecks = new ConcurrentHashMap<>();
47-
private static final long STATUS_CHECK_THROTTLE_MS = 2000; // 2 segundos
48-
49-
public AuthController(FoodItemService foodItemService, TokenService tokenService, UserRepository userRepository) {
50-
this.foodItemService = foodItemService;
51-
this.tokenService = tokenService;
35+
public AuthController(UserRepository userRepository) {
5236
this.userRepository = userRepository;
5337
}
5438

55-
5639
/**
57-
* Verificar informações do usuário atual
58-
* Endpoint alternativo para obter dados do usuário autenticado
40+
* Get current authenticated user information
5941
*/
6042
@GetMapping("/me")
6143
public ResponseEntity<UserDTO> getCurrentUser(Authentication authentication) {
@@ -84,7 +66,7 @@ public ResponseEntity<UserDTO> getCurrentUser(Authentication authentication) {
8466
}
8567

8668
/**
87-
* Endpoint para logout
69+
* Logout endpoint
8870
*/
8971
@PostMapping("/logout")
9072
public ResponseEntity<Map<String, String>> logout(HttpServletRequest request,
@@ -99,7 +81,7 @@ public ResponseEntity<Map<String, String>> logout(HttpServletRequest request,
9981
}
10082

10183
/**
102-
* Endpoint para obter URL de login do Google
84+
* Get Google OAuth2 login URL
10385
*/
10486
@GetMapping("/login/google")
10587
public ResponseEntity<Map<String, String>> getGoogleLoginUrl() {
@@ -110,189 +92,35 @@ public ResponseEntity<Map<String, String>> getGoogleLoginUrl() {
11092
}
11193

11294
/**
113-
* Verificar se o usuário está autenticado e retornar detalhes
114-
* Usado pelo frontend para verificar autenticação persistente
95+
* Check authentication status
11596
*/
11697
@GetMapping("/status")
117-
public ResponseEntity<?> authStatus(HttpServletRequest request) {
118-
log.info("=== AUTH STATUS CHECK ===");
119-
log.info("Request URI: {}, Method: {}", request.getRequestURI(), request.getMethod());
120-
log.info("Remote IP: {}, User-Agent: {}", request.getRemoteAddr(), request.getHeader("User-Agent"));
121-
log.info("Host: {}, Origin: {}, Referer: {}",
122-
request.getHeader("Host"),
123-
request.getHeader("Origin"),
124-
request.getHeader("Referer"));
98+
public ResponseEntity<Map<String, Object>> authStatus(Authentication authentication) {
99+
log.debug("Checking authentication status");
100+
101+
if (authentication != null && authentication.isAuthenticated() &&
102+
!"anonymousUser".equals(authentication.getPrincipal())) {
103+
104+
log.debug("User is authenticated: {}", authentication.getName());
105+
106+
if (authentication.getPrincipal() instanceof OAuth2User oauth2User) {
107+
String email = oauth2User.getAttribute("email");
108+
String name = oauth2User.getAttribute("name");
109+
String picture = oauth2User.getAttribute("picture");
125110

126-
try {
127-
// Get session ID or IP if no session exists
128-
HttpSession session = request.getSession(false);
129-
String sessionId = session != null ? session.getId() : request.getRemoteAddr();
130-
131-
// Check request frequency for this endpoint
132-
long now = System.currentTimeMillis();
133-
Long lastCheck = lastStatusChecks.get(sessionId);
134-
135-
// If last check was too recent, return 429 Too Many Requests
136-
if (lastCheck != null && now - lastCheck < STATUS_CHECK_THROTTLE_MS) {
137-
log.warn("Too many requests to /api/auth/status from {}", sessionId);
138-
return ResponseEntity.status(HttpStatus.TOO_MANY_REQUESTS)
139-
.body(Map.of("error", "Too many requests. Please wait."));
140-
}
141-
142-
// Update last check timestamp
143-
lastStatusChecks.put(sessionId, now);
144-
145-
// Limit cache size (remove old entries with 1% probability)
146-
if (lastStatusChecks.size() > 1000 && Math.random() < 0.01) {
147-
cleanOldEntries();
148-
}
149-
150-
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
151-
152-
// Debug session information
153-
log.debug("Auth status check with session ID: {}", sessionId);
154-
log.debug("Request URI: {}, Method: {}", request.getRequestURI(), request.getMethod());
155-
log.debug("User-Agent: {}", request.getHeader("User-Agent"));
156-
log.debug("Origin: {}", request.getHeader("Origin"));
157-
log.debug("Referer: {}", request.getHeader("Referer"));
158-
159-
// Check cookies for diagnostics
160-
Cookie[] cookies = request.getCookies();
161-
if (cookies == null) {
162-
log.debug("No cookies present in status request");
163-
} else {
164-
boolean foundJsessionId = false;
165-
for (Cookie cookie : cookies) {
166-
if ("JSESSIONID".equals(cookie.getName())) {
167-
foundJsessionId = true;
168-
log.debug("JSESSIONID cookie found: domain={}, path={}, maxAge={}, secure={}, httpOnly={}",
169-
cookie.getDomain() != null ? cookie.getDomain() : "default",
170-
cookie.getPath(),
171-
cookie.getMaxAge(),
172-
cookie.getSecure(),
173-
cookie.isHttpOnly());
174-
}
175-
}
176-
if (!foundJsessionId) {
177-
log.warn("JSESSIONID cookie not found in status request");
178-
}
179-
}
180-
181-
// Create session if it doesn't exist to ensure we have a valid session for all requests
182-
if (session == null) {
183-
session = request.getSession(true);
184-
log.debug("Created new session for auth status check: {}", session.getId());
185-
}
186-
187-
if (auth != null && auth.isAuthenticated() && !"anonymousUser".equals(auth.getPrincipal())) {
188-
log.info("User is authenticated as: {}", auth.getName());
189-
190-
try {
191-
// Ensure security context is in session
192-
session.setAttribute("SPRING_SECURITY_CONTEXT", SecurityContextHolder.getContext());
193-
194-
// Get current user info from service
195-
User user = foodItemService.getUserForTesting();
196-
if (user != null) {
197-
log.debug("Successfully retrieved user details for: {}", user.getEmail());
198-
return ResponseEntity.ok(Map.of(
199-
"authenticated", true,
200-
"user", Map.of(
201-
"id", user.getId(),
202-
"name", user.getFirstName() != null && user.getLastName() != null ?
203-
user.getFirstName() + " " + user.getLastName() : user.getEmail(),
204-
"email", user.getEmail(),
205-
"picture", user.getProfilePicture() != null ? user.getProfilePicture() : ""
206-
),
207-
"sessionId", session.getId(),
208-
"sessionCreatedAt", new java.util.Date(session.getCreationTime()).toString()
209-
));
210-
} else {
211-
log.warn("User object is null for authenticated user: {}", auth.getName());
212-
return ResponseEntity.ok(Map.of(
213-
"authenticated", true,
214-
"sessionId", session.getId(),
215-
"error", "User details unavailable",
216-
"message", "Session is authenticated but user details cannot be retrieved"
217-
));
218-
}
219-
} catch (NullPointerException e) {
220-
log.error("NullPointerException in auth status check", e);
221-
// Return basic authentication info without user details
222-
return ResponseEntity.ok(Map.of(
223-
"authenticated", true,
224-
"sessionId", session.getId(),
225-
"error", "User details unavailable",
226-
"message", "Session is authenticated but user details cannot be retrieved"
227-
));
228-
} catch (Exception e) {
229-
log.error("Error getting user details: {}", e.getMessage(), e);
230-
return ResponseEntity.ok(Map.of(
231-
"authenticated", true,
232-
"sessionId", session.getId(),
233-
"error", "Error fetching user details",
234-
"message", e.getMessage()
235-
));
236-
}
237-
} else {
238-
log.debug("User is not authenticated. Auth: {}", auth);
239-
}
240-
241-
return ResponseEntity.ok(Map.of(
242-
"authenticated", false,
243-
"sessionId", sessionId
244-
));
245-
} catch (Exception e) {
246-
log.error("Unexpected error in auth status endpoint", e);
247-
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
248-
.body(Map.of(
249-
"error", "Internal server error",
250-
"message", e.getMessage(),
251-
"path", request.getRequestURI()
252-
));
253-
}
254-
}
255-
256-
/**
257-
* Manually trigger token refresh if needed
258-
* This can be called by the frontend to ensure tokens are valid
259-
* @param request the HTTP request
260-
* @return Success status of the refresh operation
261-
*/
262-
@PostMapping("/refresh")
263-
public ResponseEntity<?> refreshToken(HttpServletRequest request) {
264-
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
265-
266-
if (auth instanceof OAuth2AuthenticationToken oauth2Auth) {
267-
try {
268-
boolean refreshed = tokenService.refreshAccessTokenIfNeeded(oauth2Auth);
269-
return ResponseEntity.ok(Map.of(
270-
"success", refreshed,
271-
"message", refreshed ? "Token refreshed successfully" : "Token refresh not needed"
272-
));
273-
} catch (Exception e) {
274-
log.error("Error refreshing token", e);
275111
return ResponseEntity.ok(Map.of(
276-
"success", false,
277-
"message", "Error refreshing token: " + e.getMessage()
112+
"authenticated", true,
113+
"user", Map.of(
114+
"email", email != null ? email : "",
115+
"name", name != null ? name : "",
116+
"picture", picture != null ? picture : ""
117+
)
278118
));
279119
}
120+
121+
return ResponseEntity.ok(Map.of("authenticated", true));
280122
}
281-
282-
return ResponseEntity.ok(Map.of(
283-
"success", false,
284-
"message", "Not an OAuth2 authentication"
285-
));
286-
}
287-
288-
/**
289-
* Limpa entradas antigas do cache de verificações de status
290-
*/
291-
private void cleanOldEntries() {
292-
long currentTime = System.currentTimeMillis();
293-
long threshold = currentTime - (STATUS_CHECK_THROTTLE_MS * 10); // 10x o tempo de throttle
294-
295-
lastStatusChecks.entrySet().removeIf(entry -> entry.getValue() < threshold);
296-
log.debug("Cache de verificações de status limpo. Tamanho atual: {}", lastStatusChecks.size());
123+
124+
return ResponseEntity.ok(Map.of("authenticated", false));
297125
}
298126
}

src/main/java/com/otavio/aifoodapp/controller/CustomErrorController.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,14 @@ public Object handleError(HttpServletRequest request) {
4343

4444
log.error("Error occurred: {} - {}, Path: {}", statusCode, errorMsg, path);
4545

46+
// Always log stacktrace if exception is present
47+
if (exception != null && exception instanceof Throwable) {
48+
StringWriter sw = new StringWriter();
49+
PrintWriter pw = new PrintWriter(sw);
50+
((Throwable) exception).printStackTrace(pw);
51+
log.error("Exception stacktrace:\n{}", sw.toString());
52+
}
53+
4654
// Verificar se a requisição está relacionada ao OAuth2
4755
if (path != null &&
4856
(path.contains("/oauth2/") ||

src/main/java/com/otavio/aifoodapp/controller/GlobalExceptionHandler.java

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
import org.springframework.web.context.request.WebRequest;
1515
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;
1616

17-
import com.otavio.aifoodapp.exception.UsernameOrPasswordInvalidExcpetion;
17+
import com.otavio.aifoodapp.exception.UsernameOrPasswordInvalidException;
1818

1919
import lombok.extern.slf4j.Slf4j;
2020

@@ -47,9 +47,9 @@ public ResponseEntity<Map<String, Object>> handleNotFoundException(UsernameNotFo
4747
/**
4848
* Tratar exceções de usuário/senha inválidos
4949
*/
50-
@ExceptionHandler(UsernameOrPasswordInvalidExcpetion.class)
50+
@ExceptionHandler(UsernameOrPasswordInvalidException.class)
5151
@ResponseStatus(HttpStatus.UNAUTHORIZED)
52-
public ResponseEntity<Map<String, Object>> handleUsernameOrPasswordInvalidException(UsernameOrPasswordInvalidExcpetion ex, WebRequest request) {
52+
public ResponseEntity<Map<String, Object>> handleUsernameOrPasswordInvalidException(UsernameOrPasswordInvalidException ex, WebRequest request) {
5353
log.warn("Invalid credentials: {}", ex.getMessage());
5454

5555
return ResponseEntity
@@ -65,19 +65,16 @@ public ResponseEntity<Map<String, Object>> handleUsernameOrPasswordInvalidExcept
6565
/**
6666
* Tratar exceções de validação de argumentos
6767
*/
68-
@ExceptionHandler(MethodArgumentNotValidException.class)
69-
@ResponseStatus(HttpStatus.BAD_REQUEST)
70-
public ResponseEntity<Map<String, Object>> handleArgumentNotValidException(MethodArgumentNotValidException ex, WebRequest request) {
68+
@Override
69+
protected ResponseEntity<Object> handleMethodArgumentNotValid(MethodArgumentNotValidException ex, org.springframework.http.HttpHeaders headers, org.springframework.http.HttpStatusCode status, WebRequest request) {
7170
log.warn("Validation failed: {}", ex.getMessage());
72-
7371
Map<String, String> fieldErrors = new HashMap<>();
7472
ex.getBindingResult().getAllErrors().forEach((error) -> {
7573
FieldError fieldError = (FieldError) error;
7674
fieldErrors.put(fieldError.getField(), fieldError.getDefaultMessage());
7775
});
78-
7976
return ResponseEntity
80-
.status(HttpStatus.BAD_REQUEST)
77+
.status(org.springframework.http.HttpStatus.BAD_REQUEST)
8178
.body(Map.of(
8279
"error", "validation_failed",
8380
"message", "Request validation failed",

src/main/java/com/otavio/aifoodapp/exception/UsernameOrPasswordInvalidExcpetion.java

Lines changed: 0 additions & 11 deletions
This file was deleted.

src/main/java/com/otavio/aifoodapp/model/FoodItem.java

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,4 +59,24 @@ public class FoodItem {
5959
@ManyToOne(fetch = FetchType.LAZY)
6060
@JoinColumn(name = "user_id")
6161
private User user;
62+
63+
public void setUser(User user) {
64+
this.user = user;
65+
}
66+
67+
public User getUser() {
68+
return user;
69+
}
70+
71+
public void setId(Long id) {
72+
this.id = id;
73+
}
74+
75+
public void setTags(List<String> tags) {
76+
this.tags = tags;
77+
}
78+
79+
public void setFoodGroup(FoodGroup foodGroup) {
80+
this.foodGroup = foodGroup;
81+
}
6282
}

0 commit comments

Comments
 (0)