Skip to content

Quarkus JWT Returning Unauthorized on deployment

I have a Quarkus project that expose some REST Endpoints. When working on dev mode or running from the local build docker container all work fine, those endpoints annotated with @RolesAllowed and @PermitAll work as supposed to be. But when I deploy to a AWS service with the following steps:

  1. Commit to Gitlab
  2. Run Gitlab CICD
  3. Send result with the config to an AWS S3 Bucket
  4. Trigger AWS codepipeline
  5. Deploy to Elastic Bean using docker image

After that the container is deployed all endpoints with @PermitAll work fine but those with @RolesAllowed responds with 401 Unauthorized, the token sent to those endpoints is valid, it is correctly formed and has not expired.

HTTP/1.1 401 Unauthorized
Date: Mon, 25 Oct 2021 15:51:01 GMT
Content-Length: 0
Connection: keep-alive
Server: nginx/1.20.0
www-authenticate: Bearer

<Response body is empty>

Response code: 401 (Unauthorized); Time: 397ms; Content length: 0 bytes

The project is running on Quarkus Platform 2.2.3.Final on JDK 11 and I’m using the following extensions

Installed features: [agroal, cdi, config-yaml, hibernate-orm, hibernate-orm-panache, hibernate-validator, jaeger, jdbc-h2, jdbc-mysql, mailer, narayana-jta, qute, rest-client, rest-client-jackson, resteasy, resteasy-jackson, security, smallrye-context-propagation, smallrye-health, smallrye-jwt, smallrye-openapi, smallrye-opentracing, swagger-ui, vertx, vertx-web]

UPDATE:

I dive into the Quarkus documentation and added a few configurations and map all authorization exceptions and get the log and stacktrace, But I don’t know why is happening as I’m using the procedure that que guide says when create the public and private key. And also if I put all the base64 token in the jwt.io debugger and I paste the public key content it says that the signature is valid.

io.quarkus.security.AuthenticationFailedException
    at io.quarkus.smallrye.jwt.runtime.auth.MpJwtValidator$1.accept(MpJwtValidator.java:61)
    at io.quarkus.smallrye.jwt.runtime.auth.MpJwtValidator$1.accept(MpJwtValidator.java:49)
    at io.smallrye.context.impl.wrappers.SlowContextualConsumer.accept(SlowContextualConsumer.java:21)
    at io.smallrye.mutiny.operators.uni.builders.UniCreateWithEmitter.subscribe(UniCreateWithEmitter.java:22)
    at io.smallrye.mutiny.operators.AbstractUni.subscribe(AbstractUni.java:36)
    at io.smallrye.mutiny.operators.uni.UniMemoizeOp.subscribe(UniMemoizeOp.java:76)
    at io.smallrye.mutiny.operators.AbstractUni.subscribe(AbstractUni.java:36)
    at io.smallrye.mutiny.operators.uni.UniOnItemTransformToUni.subscribe(UniOnItemTransformToUni.java:25)
    at io.smallrye.mutiny.operators.AbstractUni.subscribe(AbstractUni.java:36)
    at io.smallrye.mutiny.operators.uni.UniOnTermination.subscribe(UniOnTermination.java:21)
    at io.smallrye.mutiny.operators.AbstractUni.subscribe(AbstractUni.java:36)
    at io.smallrye.mutiny.operators.uni.UniMemoizeOp.subscribe(UniMemoizeOp.java:76)
    at io.smallrye.mutiny.operators.AbstractUni.subscribe(AbstractUni.java:36)
    at io.smallrye.mutiny.operators.uni.UniBlockingAwait.await(UniBlockingAwait.java:54)
    at io.smallrye.mutiny.groups.UniAwait.atMost(UniAwait.java:61)
    at io.smallrye.mutiny.groups.UniAwait.indefinitely(UniAwait.java:42)
    at io.quarkus.security.runtime.SecurityIdentityAssociation.getIdentity(SecurityIdentityAssociation.java:66)
    at io.quarkus.security.runtime.SecurityIdentityAssociation_ClientProxy.getIdentity(SecurityIdentityAssociation_ClientProxy.zig:250)
    at io.quarkus.security.runtime.SecurityIdentityProxy.hasRole(SecurityIdentityProxy.java:38)
    at io.quarkus.security.runtime.SecurityIdentityProxy_ClientProxy.hasRole(SecurityIdentityProxy_ClientProxy.zig:401)
    at io.quarkus.security.runtime.interceptor.check.RolesAllowedCheck.apply(RolesAllowedCheck.java:55)
    at io.quarkus.security.runtime.interceptor.SecurityConstrainer.check(SecurityConstrainer.java:28)
    at io.quarkus.security.runtime.interceptor.SecurityHandler.handle(SecurityHandler.java:23)
    at io.quarkus.security.runtime.interceptor.RolesAllowedInterceptor.intercept(RolesAllowedInterceptor.java:29)
    at io.quarkus.security.runtime.interceptor.RolesAllowedInterceptor_Bean.intercept(RolesAllowedInterceptor_Bean.zig:386)
    at io.quarkus.arc.impl.InterceptorInvocation.invoke(InterceptorInvocation.java:41)
    at io.quarkus.arc.impl.AroundInvokeInvocationContext.perform(AroundInvokeInvocationContext.java:41)
    at io.quarkus.arc.impl.InvocationContexts.performAroundInvoke(InvocationContexts.java:32)
    at za.co.tenfour.controller.UserController_Subclass.findMyself(UserController_Subclass.zig:1031)
    at za.co.tenfour.controller.UserController_ClientProxy.findMyself(UserController_ClientProxy.zig:262)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:566)
    at org.jboss.resteasy.core.MethodInjectorImpl.invoke(MethodInjectorImpl.java:170)
    at org.jboss.resteasy.core.MethodInjectorImpl.invoke(MethodInjectorImpl.java:130)
    at org.jboss.resteasy.core.ResourceMethodInvoker.internalInvokeOnTarget(ResourceMethodInvoker.java:660)
    at org.jboss.resteasy.core.ResourceMethodInvoker.invokeOnTargetAfterFilter(ResourceMethodInvoker.java:524)
    at org.jboss.resteasy.core.ResourceMethodInvoker.lambda$invokeOnTarget$2(ResourceMethodInvoker.java:474)
    at org.jboss.resteasy.core.interception.jaxrs.PreMatchContainerRequestContext.filter(PreMatchContainerRequestContext.java:364)
    at org.jboss.resteasy.core.ResourceMethodInvoker.invokeOnTarget(ResourceMethodInvoker.java:476)
    at org.jboss.resteasy.core.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:434)
    at org.jboss.resteasy.core.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:408)
    at org.jboss.resteasy.core.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:69)
    at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:492)
    at org.jboss.resteasy.core.SynchronousDispatcher.lambda$invoke$4(SynchronousDispatcher.java:261)
    at org.jboss.resteasy.core.SynchronousDispatcher.lambda$preprocess$0(SynchronousDispatcher.java:161)
    at org.jboss.resteasy.core.interception.jaxrs.PreMatchContainerRequestContext.filter(PreMatchContainerRequestContext.java:364)
    at org.jboss.resteasy.core.SynchronousDispatcher.preprocess(SynchronousDispatcher.java:164)
    at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:247)
    at io.quarkus.resteasy.runtime.standalone.RequestDispatcher.service(RequestDispatcher.java:73)
    at io.quarkus.resteasy.runtime.standalone.VertxRequestHandler.dispatch(VertxRequestHandler.java:138)
    at io.quarkus.resteasy.runtime.standalone.VertxRequestHandler$1.run(VertxRequestHandler.java:93)
    at io.quarkus.vertx.core.runtime.VertxCoreRecorder$13.runWith(VertxCoreRecorder.java:543)
    at org.jboss.threads.EnhancedQueueExecutor$Task.run(EnhancedQueueExecutor.java:2449)
    at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1478)
    at org.jboss.threads.DelegatingRunnable.run(DelegatingRunnable.java:29)
    at org.jboss.threads.ThreadLocalResettingRunnable.run(ThreadLocalResettingRunnable.java:29)
    at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
    at java.base/java.lang.Thread.run(Thread.java:829)nCaused by: io.smallrye.jwt.auth.principal.ParseException: SRJWT07000: Failed to verify a token
    at io.smallrye.jwt.auth.principal.DefaultJWTTokenParser.parseClaims(DefaultJWTTokenParser.java:166)
    at io.smallrye.jwt.auth.principal.DefaultJWTTokenParser.parse(DefaultJWTTokenParser.java:56)
    at io.smallrye.jwt.auth.principal.DefaultJWTCallerPrincipalFactory.parse(DefaultJWTCallerPrincipalFactory.java:31)
    at io.smallrye.jwt.auth.principal.DefaultJWTParser.parse(DefaultJWTParser.java:60)
    at io.smallrye.jwt.auth.principal.DefaultJWTParser_ClientProxy.parse(DefaultJWTParser_ClientProxy.zig:298)
    at io.quarkus.smallrye.jwt.runtime.auth.MpJwtValidator$1.accept(MpJwtValidator.java:53)
    ... 59 morenCaused by: org.jose4j.jwt.consumer.InvalidJwtSignatureException: JWT rejected due to invalid signature. Additional details: [[9] Invalid JWS Signature: JsonWebSignature{"typ":"JWT","alg":"RS256"}->eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJodHRwczovLzEwZm91ci5jby56YS9pc3N1ZXIiLCJncm91cHMiOlsiQWRtaW5pc3RyYXRvciJdLCJpYXQiOjE2MzUxOTcyMjUsImV4cCI6MTY2Njc1NDgyNSwidXBuIjoic3VwcG9ydEAxMGZvdXIuY28uemEiLCJ1aWQiOjEsImZ1bGxfbmFtZSI6IjEwRm91ciBBZG1pbmlzdHJhdG9yIiwianRpIjoiMWZmNDA4MjYtNmNhNy00NzQxLTk0Y2QtOTlhY2Q0ZGY4M2IyIn0.i8skIOrh6Et84uuASjwhQQMnIjRnfiP4zjPQAB7XsyVwSEuZEc31m9reXTW1uWjzvoZTbllYZ79Aiu4Vq7MqMPJKvXIqXhmTWbX-R9JnQoI0M_Sb1c6DAXDD_biZGYUM48-FO3lRG9XCu_qr06uT4sOb1DLwh6pjdehTwnkbMX_B1Jckn-7hStCcV04XwXOOZkZWnP6_uFltsDLIQd7PsYbnRzgO19xhaAb6sfuUeKwhTs2WJL7msj0FhH_HbeEFel5OnH_NLfPhEvzByS5VRdN9j3LRNZ5z0OOm3rK_W9A6r34VRaUvddJ2_wXiyv3hvtMKLoN8wJUJuhhjqQKtTQ]
    at org.jose4j.jwt.consumer.JwtConsumer.processContext(JwtConsumer.java:224)
    at org.jose4j.jwt.consumer.JwtConsumer.process(JwtConsumer.java:433)
    at io.smallrye.jwt.auth.principal.DefaultJWTTokenParser.parseClaims(DefaultJWTTokenParser.java:145)
    ... 64 moren

Answer

The problem was the AWS time zone configuration, for some strange reason it interfered with how Quarkus validates the IAT and EXP claims of the token. I removed the AWS time zone settings and set the zone using a system property at the JVM level and the problem was completely fixed.