From 7041fc206b1d775ad2b60222a41e7cd18c943d0c Mon Sep 17 00:00:00 2001 From: Daniel Arroyo Date: Sat, 15 Aug 2026 01:26:46 -0400 Subject: [PATCH] fix(auth): drop @Authenticated/@RolesAllowed on cookie-based endpoints Quarkus security solo lee JWT del header 'Authorization: Bearer ...' por defecto. Esta app entrega el JWT en una cookie HttpOnly ('auth-token'), entonces @Authenticated/@RolesAllowed rebotaban con 401 antes de que el endpoint pudiera validar manualmente. Reemplazos: - @Authenticated en /change-password: sacada. El endpoint ya hace jwtCookieAuth.extractToken() y devuelve 401 manual si falla. - @Authenticated en /check: sacada. Endpoint ahora extrae y valida el JWT a mano. - @Authenticated en /me: never estuvo, OK. - @RolesAllowed('admin') en /admin/reset-password y /admin/users: reemplazada por chequeo manual jwt.getGroups().contains('admin'), devolviendo 403 si no es admin. Sigue funcionando el MustChangePasswordFilter porque sigue siendo un ContainerRequestFilter con @Priority(AUTHENTICATION+100) y no requiere auth previa para correr: simplemente aborta con 403 si mustChangePassword=true salvo allowlist (change-password, logout). Removida inyeccion no usada de currentJwt (JsonWebToken) que solo estaba para @RolesAllowed. --- .../java/com/l2/shots/auth/AuthResource.java | 30 +++++++++++-------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/src/main/java/com/l2/shots/auth/AuthResource.java b/src/main/java/com/l2/shots/auth/AuthResource.java index 512b814..afec130 100644 --- a/src/main/java/com/l2/shots/auth/AuthResource.java +++ b/src/main/java/com/l2/shots/auth/AuthResource.java @@ -1,7 +1,5 @@ package com.l2.shots.auth; -import io.quarkus.security.Authenticated; -import jakarta.annotation.security.RolesAllowed; import jakarta.inject.Inject; import jakarta.ws.rs.Consumes; import jakarta.ws.rs.GET; @@ -19,6 +17,12 @@ import org.eclipse.microprofile.jwt.JsonWebToken; import java.util.Optional; import java.util.UUID; +/** + * No usa @Authenticated / @RolesAllowed porque Quarkus security por defecto + * solo lee el JWT del header "Authorization: Bearer ...". Esta app entrega + * el JWT en una cookie HttpOnly, asi que la validacion la hacemos a mano + * via JwtCookieAuth en cada endpoint que requiere auth. + */ @Path("/api/auth") @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) @@ -30,9 +34,6 @@ public class AuthResource { @Inject JwtCookieAuth jwtCookieAuth; - @Inject - JsonWebToken currentJwt; - @ConfigProperty(name = "app.auth.cookie-name") String cookieName; @@ -81,7 +82,6 @@ public class AuthResource { @POST @Path("/change-password") - @Authenticated public Response changePassword(@Context HttpHeaders headers, ChangePasswordRequest body) { if (body == null || body.currentPassword() == null || body.newPassword() == null) { return Response.status(400).entity(new ErrorBody("Faltan campos requeridos")).build(); @@ -114,14 +114,16 @@ public class AuthResource { @POST @Path("/admin/reset-password") - @RolesAllowed("admin") - public Response adminResetPassword(AdminResetPasswordRequest body) { + public Response adminResetPassword(@Context HttpHeaders headers, AdminResetPasswordRequest body) { if (body == null || body.username() == null || body.newPassword() == null) { return Response.status(400).entity(new ErrorBody("Faltan campos requeridos")).build(); } + Optional jwt = jwtCookieAuth.extractToken(headers); + if (jwt.isEmpty()) return Response.status(401).build(); + if (!jwt.get().getGroups().contains("admin")) return Response.status(403).build(); UUID requesterId; try { - requesterId = UUID.fromString(currentJwt.getSubject()); + requesterId = UUID.fromString(jwt.get().getSubject()); } catch (Exception e) { return Response.status(401).build(); } @@ -146,15 +148,17 @@ public class AuthResource { @GET @Path("/admin/users") - @RolesAllowed("admin") - public Response adminListUsers() { + public Response adminListUsers(@Context HttpHeaders headers) { + Optional jwt = jwtCookieAuth.extractToken(headers); + if (jwt.isEmpty()) return Response.status(401).build(); + if (!jwt.get().getGroups().contains("admin")) return Response.status(403).build(); return Response.ok(authService.listUsersForAdmin()).build(); } @GET @Path("/check") - @Authenticated - public Response check() { + public Response check(@Context HttpHeaders headers) { + if (jwtCookieAuth.extractToken(headers).isEmpty()) return Response.status(401).build(); return Response.ok().build(); }