graphqljwtquarkussmallrye

Quarkus GraphQL Authentication


I havea Quarkus Java application. It serves my GraphQL API.

I would like to authenticate requests with JsonWebTokens (JWT). I know that smallrye-graphql already has some built-in JWT functionality. But I need to extend that. I need to add a custom role to authenticated users and other customizations.

How can I tap into that? According to this Quarksu-Security documentation I tried to implement my own HttpAuthenticationMechanism.

I have questions. See comments in code:

@Alternative
@Priority(1)  //  <======= io.quarkus.arc.Priority   or   javax.annotation.Priority ???????
@ApplicationScoped
public class MyCustomAuthMechanism implements HttpAuthenticationMechanism {
    @Override
    public Uni<SecurityIdentity> authenticate(RoutingContext context, IdentityProviderManager identityProviderManager) {
        // ==========================
        // This is what i am trying to archive. But this whole method is never called ???
        // ==========================
        QuarkusSecurityIdentity user = QuarkusSecurityIdentity.builder().addRole("CUSTOM_USER").build();

       // This would be nice if it would be possible to access the JWT directly
       //context.request().getHeader(HttpHeaderNames.AUTHORIZATION);

       return Uni.createFrom().item(user);
    }

    @Override
    public Uni<ChallengeData> getChallenge(RoutingContext context) {
        return Uni.createFrom().item(new ChallengeData(HttpResponseStatus.UNAUTHORIZED.code(), HttpHeaderNames.AUTHORIZATION, null));       // <====== is this correct? What to return here?
    }

    @Override
    public Set<Class<? extends AuthenticationRequest>> getCredentialTypes() {
        // This is called, but this seems to be the problem? What to return here?????
        return Collections.singleton(TokenAuthenticationRequest.class);
    }
}

Thank you very much for any advice or even better example code. (ChatGPT has some, but very old outdated versions :-)

Or to phrase my question in a more general way: My client will send HTTP reqests with a "Authorization: Bearer ...JWT...data..." header. That JWT is signed and contains information that I need in the backend. What's the easiest way to fetch that information in Quarkus GraphQL ?

Update 1

Tried another way. Without any custom class or anythign. Just default as described by Quarkus docs.

application.properties

Do I need to set only one or both of these properties???

smallrye.jwt.sign.key.location=META-INF/resources/jwtKey.json
smallrye.jwt.verify.key.location=META-INF/resources/jwtKey.json

META-INF/resources/jwtKey.json

{
  "keys": [
    {
      "kty":"oct",
      "kid":"secretKey",
      "k":"AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ-EstJQLr_T-1qS0ga............Zr1Z9CAow"
    }
  ]
}

And I create my JWT like this:

String JWT = Jwt.issuer("my_site")
                .subject("TestUser11@liquido.vote")
                .upn("upn@liquido.vote")
                .groups(Collections.singleton("LIQUIDO_USER"))
                .sign()

My GraphQL @Query is simply annotated with @Authenticated. But I get the error:

ERROR [io.sma.graphql] (vert.x-eventloop-thread-1) SRGQL012000: Data Fetching Error: io.quarkus.security.UnauthorizedException

Thanks for any hints or suggestions.


Solution

  • In the end this turned out to be simply about having the correct configuration and using the quarkus default security architecture.

    No extra classes needed.

    Put your symetric keys under META-INF/resources and then configure

    application.properties

    # key for signing JWTs (we use a simple symmetric key)
    smallrye.jwt.sign.key.location=liquidoJwtKey.json
    smallrye.jwt.verify.key.location=liquidoJwtKey.json
    smallrye.jwt.verify.algorithm=HS256
    

    After login the server sends a self signed JWT back to the client. The client can then use this JWT to authenticate in future requests. The JWT will automatically be evaluated by quarkus. And you can configure authorization via annotations, e.g. in some controller

    @RolesAllowed("admin")
    public void adminOnlyMethod() { ... }