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'
Last modified on 2023-10-28