Quarkus + Auth0 , the missing guide

Minor edits on October 28th 2023: some tipos and shortened a bit the content when migrating the blog.

The situation

I am currently playing with @QuarkusIO (for the native app promise) and @kotlin and was looking to integrate it with an IAM using JWT. And because I am generous and don’t want you to spend as much time on this, may the following be of some help. So I chose @auth0 for the popular social networks integrations, the great support and tons of great guides, and the free offer for a solid authentication and authorization service. Unfortunately for the lazy me, I did not find a straightforward guide. There are great guides for Spring and native backend but obviously nothing that could help me quickly. If you are only interested by the solution, just click here.

And excitement almost became panic

Yeah, great, time to start a little sidequest. But not too long because, hey, I still have to get back to my primary purpose. So first I created a native application on Auth0 to support my solution.

Then it was time to make it work with my generated Quarkus backend. Quarkus’ documentation is mostly easy to use. But when it came to the authentication/security settings, I was kind of lost at first. So lost, that at a point, I considered writing my own custom filter to do this. But I (almost) immediately felt that it was overkill and that there should be something simpler out there.

And then I chilled down, remembered that I was tired (never work tired, just never), and also thought that the people behind the framework were too smart not to provide easy ways to validate a token.

Enough blabbering, the facts!

At this point, I already spent 3 to 4 x 3h looking for a viable solution. I then encountered my first pain point.

Debugging ability is a life savior

This is when after a few searches and tricks not worthy to mention here, I found this Github issue smallrye-jwt not working in native mode · Issue #1163 · quarkusio/quarkus · GitHub that retrospectively was the first step toward resolution. After reading the changes and analyzing what I should do, I decided that I would debug a little to understand what was wrong with my settings.
Usually, debugging is as easy as clicking a button on my favorite Jetbrains IDE. It didn’t work and I still don’t know why, because I didn’t take time to dig deeper. But I found an alternative, run the Quarkus application, then attach a debugger. A little more effort but nothing insurmountable.

So what was wrong

This is really where you should pay attention 😃
I first amended my pom to get the most recent available version for Quarkus (0.19.1 at the time).
As I debugged the applications and added enough logs for the targeted category (io.quarkus.elytron, io.smallrye.jwt), I found the following to be able to read the JWT:

  • You need the public key of the certificate used to sign it. You can find the certificate in the settings of your Auth0 application. Just scroll until you see the Show Advanced Settings link. Click on it, access the Certificates tab, download it with the Download certificate button. I spent too much time using the certificate instead of its public key. Be smarter. You can use this command to extract it openssl x509 -pubkey -noout -in cert.pem > pubkey.pem.
  • The mp.jwt.verify.issuer is the Domain of your Auth0 application.

Auth0 integration guide

The relevant parts of the solution.

Create a native application on Auth0Go to the Auth0 application settings, scroll until you see the Show Advanced Settings link. Click on it, access the Certificates tab, download it with the Download certificate button. Don’t forget to extract the public key to the appropriate location openssl x509 -pubkey -noout -in cert.pem > pubkey.pem. An extract of the pom.xml below.

<properties>
    <quarkus.version>0.19.1</quarkus.version>
    ...
</properties>

Extract of the security settings of the application.properties file

mp.jwt.verify.publickey.location=META-INF/resources/pubkey.pem
mp.jwt.verify.issuer=https://dev-******.eu.auth0.com/

quarkus.smallrye-jwt.auth-mechanism=MP-JWT
quarkus.smallrye-jwt.enabled=true

Defining the REST resource used to test

@Path("/test")
@RequestScoped
class TestJWTService {
    @GET
    @PermitAll
    @Produces(MediaType.TEXT_PLAIN)
    fun hello(@Context ctx: SecurityContext): String {
        val caller = ctx.userPrincipal
        val name = if (caller == null) "anonymous" else caller.name
        val isSecure = ctx.isSecure
        val authScheme = ctx.authenticationScheme
        return "hello + $name, isSecure: $isSecure, authScheme: $authScheme"
    }
}

Get the token

curl --request POST \
--url https://dev-bhmhh10f.eu.auth0.com/oauth/token \
--header 'content-type: application/json' \
--data '{"client_id":"your_client_id","client_secret":"your_client_secret","audience":"the_audience","grant_type":"client_credentials"}'

Test the token you just got
curl --request GET --url //localhost:8080/bags --header 'authorization: Bearer the_token_you_just_got'

Original documentation


Last modified on 2023-10-28