fix(auth): drop @Authenticated/@RolesAllowed on cookie-based endpoints
CI / Build Native (push) Successful in 6m14s

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.
This commit is contained in:
2026-08-15 01:26:46 -04:00
parent cc09fe8943
commit 7041fc206b
@@ -1,7 +1,5 @@
package com.l2.shots.auth; package com.l2.shots.auth;
import io.quarkus.security.Authenticated;
import jakarta.annotation.security.RolesAllowed;
import jakarta.inject.Inject; import jakarta.inject.Inject;
import jakarta.ws.rs.Consumes; import jakarta.ws.rs.Consumes;
import jakarta.ws.rs.GET; import jakarta.ws.rs.GET;
@@ -19,6 +17,12 @@ import org.eclipse.microprofile.jwt.JsonWebToken;
import java.util.Optional; import java.util.Optional;
import java.util.UUID; 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") @Path("/api/auth")
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON)
@@ -30,9 +34,6 @@ public class AuthResource {
@Inject @Inject
JwtCookieAuth jwtCookieAuth; JwtCookieAuth jwtCookieAuth;
@Inject
JsonWebToken currentJwt;
@ConfigProperty(name = "app.auth.cookie-name") @ConfigProperty(name = "app.auth.cookie-name")
String cookieName; String cookieName;
@@ -81,7 +82,6 @@ public class AuthResource {
@POST @POST
@Path("/change-password") @Path("/change-password")
@Authenticated
public Response changePassword(@Context HttpHeaders headers, ChangePasswordRequest body) { public Response changePassword(@Context HttpHeaders headers, ChangePasswordRequest body) {
if (body == null || body.currentPassword() == null || body.newPassword() == null) { if (body == null || body.currentPassword() == null || body.newPassword() == null) {
return Response.status(400).entity(new ErrorBody("Faltan campos requeridos")).build(); return Response.status(400).entity(new ErrorBody("Faltan campos requeridos")).build();
@@ -114,14 +114,16 @@ public class AuthResource {
@POST @POST
@Path("/admin/reset-password") @Path("/admin/reset-password")
@RolesAllowed("admin") public Response adminResetPassword(@Context HttpHeaders headers, AdminResetPasswordRequest body) {
public Response adminResetPassword(AdminResetPasswordRequest body) {
if (body == null || body.username() == null || body.newPassword() == null) { if (body == null || body.username() == null || body.newPassword() == null) {
return Response.status(400).entity(new ErrorBody("Faltan campos requeridos")).build(); return Response.status(400).entity(new ErrorBody("Faltan campos requeridos")).build();
} }
Optional<JsonWebToken> 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; UUID requesterId;
try { try {
requesterId = UUID.fromString(currentJwt.getSubject()); requesterId = UUID.fromString(jwt.get().getSubject());
} catch (Exception e) { } catch (Exception e) {
return Response.status(401).build(); return Response.status(401).build();
} }
@@ -146,15 +148,17 @@ public class AuthResource {
@GET @GET
@Path("/admin/users") @Path("/admin/users")
@RolesAllowed("admin") public Response adminListUsers(@Context HttpHeaders headers) {
public Response adminListUsers() { Optional<JsonWebToken> 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(); return Response.ok(authService.listUsersForAdmin()).build();
} }
@GET @GET
@Path("/check") @Path("/check")
@Authenticated public Response check(@Context HttpHeaders headers) {
public Response check() { if (jwtCookieAuth.extractToken(headers).isEmpty()) return Response.status(401).build();
return Response.ok().build(); return Response.ok().build();
} }