Simple JWT Token authentication with Quarkus

Dmytro Chaban
May 12, 2020 · 6 min read
Image for post
Image for post

Every significant app will have security. And you probably opened this article because you’re looking for some simple yet durable solution. What can be simpler yet good enough as JWT Token authorization? Official Quarkus website already covered this topic, but I’ll show the most popular use-case for it, that is, generation of the token when the user logs in. I’ll show you a more abstract solution that you can plug-in to any app and generate a token with a single line of code in your /login method.

I also covered this topic at my Quarkus course in Security section. This articles basically repeats same lesson with additional simplicity

User flow

We’ll take the most usual use case: Users open your app, users register, then they do log in, receive the token, and after that use your app as they please.

Initial setup

You can init empty project from scratch by using next command:

mvn io.quarkus:quarkus-maven-plugin:1.4.2.Final:create \
-DprojectGroupId=tech.donau.quarkify \
-DprojectArtifactId=jwt-auth \
-DclassName="tech.donau.quarkify.ExampleResource" \
-Dpath="/hello" \
-Dextensions="resteasy-jsonb, jwt, jdbc-h2, hibernate-orm-panache"

Or, if you already have some project, you can add next dependencies into pom.xml

<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-resteasy-jsonb</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-smallrye-jwt</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-jdbc-h2</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-hibernate-orm-panache</artifactId>
</dependency>

Complete working solution, as always, available at our github.

Static classes

Let’s start by adding TokenUtils class, it's independent so you can include it into any project.

You’re only interested in generateTokenString method. This method will be executed by our future TokenService. You can trace what it's doing, but basically, from an abstraction point of view, this method will encode our JwtClaims object, sign it with a private key that we will generate in next steps(one command line), and return to us it back as a string that we can pass to the user.

Other methods can be handy for some specific use-case, we won’t touch them in this article.

Token service

Now, once we have prepared class that will handle all underlying logic, we can create service that will be used by our services and resources.

JWT embeds idea that every user has role. You can, for example, have user, service and admin roles. Users are basic users of your app, they only have access to user endpoints. We can say that services are bots or applications that have access to limited amount of endpoints. One example can be that service can only upload data, it can’t read or delete it. And admin, of course, is god, he has access to such endpoints that are not available to simple users.

Let’s create a static class with constants for them. This will help you in future if you accidentally will put user instead of User for some endpoint and all the users won't have the ability to execute this endpoint.

And now, let’s create final class, which we’ll use to generate token

You can see that we have generateUserToken() method that calls generateToken(). The last method executes our TokenUtils.generateTokenString() method, which returns the token.

Private and Public keys

In order to JWT to work, we need to sign it with private key. Without this step anybody can just pass fake data to our Quarkus app, and it will think that it’s valid credentials. This way anybody can use admin endpoints, for example. Luckly get private and public key is super easy:

openssl genrsa -out publicKey.pem
openssl pkcs8 -topk8 -inform PEM -in publicKey.pem -out privateKey.pem -nocrypt
openssl rsa -in publicKey.pem -pubout -outform PEM -out publicKey.pem

This will create two files, which you can move into src/resources folder. Beware that if you loose this files, users that already generated tokens will be declined in authentication. I would suggest to keep them somewhere safe. Don't share them with anyone as well.

Enable JWT Security

When you moved publicKey.pem and privateKey.pem into src/resources, TokenService will be already usable, but we also need to pass some application properties in order for Quarkus to know where to look for our publicKey.pem. Put next data into application.properties

quarkus.smallrye-jwt.enabled=true
mp.jwt.verify.publickey.location=publicKey.pem
mp.jwt.verify.issuer=DonauTech

Adding persistency

Let’s add User object for our whole article to be usable. I'll use h2 database with Hibernate and Panache to make it as small as possible.

Firstly, let’s create User, as in next example. Keep in mind that it's super simple example, even without password encryption, this is not a production solution 🙂

Let’s also add some more parameters into application.properties

quarkus.datasource.url=jdbc:h2:file:./test quarkus.datasource.driver=org.h2.Driver quarkus.datasource.username=test quarkus.datasource.password=test quarkus.hibernate-orm.database.generation=update quarkus.hibernate-orm.dialect=org.hibernate.dialect.H2Dialect

This is a super simple file database. It’ll be located at target/test.db. We use update, which will update schema without deletion of it on restart

Registration and login

We now ready to finally do registration and login. Ideally, we can have UserService, but for this example I just put everything into UserResource

There’s nothing special here.

1. We have /register method that just persists the user itself into the database.
2. We have /login method that checks if data is correct, and after that generates a token back to user. Here's a homework for you: make it return a {"token": data} structure so it's easy to use from javascript.

Secure regular endpoints

Security is not a security if it secures nothing. Let’s create few endpoints to test our token. We already have ExampleResource, let's expand it a little bit.

Let’s go one method at a time:

Test time

Now it’s time to do full test cycle and see how it’ll work. I’ll use curl commands with postman screenshots.

curl --location --request POST 'http://localhost:8080/users/register' \
--header 'Content-Type: application/json' \
--data-raw '{
"login": "testuser",
"email": "testuser@gmail.com",
"password": "testtest"
}'

This will return same result, but you’ll also have your id.

Image for post
Image for post

Now let’s log into our application by getting JWT token that we can later use in any other endpoint

curl --location --request GET 'http://localhost:8080/users/login?login=testuser&password=testtest'
Image for post
Image for post

Let’s try to execute /hello without any token, it should work

curl --location --request GET 'http://localhost:8080/hello/'
Image for post
Image for post

Now, put our token into header so that key is Authorization and value is Bearer $token

Image for post
Image for post

Now we can execute /hello/me endpoint, you should see user object in output

curl --location --request GET 'http://localhost:8080/hello/me' \
--header 'Authorization: Bearer $YOURTOKEN'
Image for post
Image for post

You can see drawback of storing password as a plain text here. Ideally you shouldn’t return password at all.

Let’s try to execute /hello/admin endpoint

curl --location --request GET 'http://localhost:8080/hello/admin' \
--header 'Authorization: Bearer $YOURTOKEN'
Image for post
Image for post

As expected, simple user won’t be able to access admin endpoints. Try generating admin token yourself and executing this endpoint

And last endpoint, /hello/void, which won't work for anyone.

curl --location --request GET 'http://localhost:8080/hello/void' \
--header 'Authorization: Bearer $YOURTOKEN'
Image for post
Image for post

In conclusion

Now you know embeddable security solution that you can re-use in any project. Of course, there are many other options and this JWT auth doesn’t fit all. But for starters and simple projects, this solution can be really easy, without adding extra services just to handle the basic flow.

Originally published at https://quarkify.net on May 12, 2020.

Quarkify

Latest Quarkus Articles, Tutorials and News

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface. Learn more

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox. Explore

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic. Write on Medium

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store