Our thoughts, knowledge, insights and opinions

User Authentication with Keycloak - Part 2: Akka HTTP backend

Welcome to our second article about Keycloak Server! Previously, we’ve learnt to setup the server and make use of it in a webapp - today, we’re going to create a matching backend API and also learn a little bit about the OAuth2/OpenID magic that powers our entire authn/authz mechanism. Let’s get started!

Fixing the frontend

I’m going to assume you’ve followed the first part of our tutorial and are thus familiar with the simple webapp we’ve created there. A few changes will be needed (feel free to checkout the modified code from https://github.com/kmikulski/blog-keycloak/tree/master/keycloak-react-part2 ) - let’s add the QueryAPI component, which we’ll use to call our backend:

As you can see, most of it isn’t too complicated - it just renders a button and below it a placeholder div which is filled with the response from the server upon query completion. The query is run against the http://localhost:9000/users endpoint (normally, you’d probably want to externalize the URL to a config file), which we’ll create in a moment.

One thing that’s of particular importance to us is the optional authorization header:

Access tokens are what Keycloak (and other OAuth2-compliant solutions) use as a means of establishing user’s identity and authorization level. They can work interchangeably either by being provided as cookies or as request headers, as you can see in the example above. We’ll talk more about this later - for now, note that this header is included optionally, based on whether or not we provide a Keycloak object to the component. We’re doing it this way so that we can later demonstrate what happens when a request is NOT properly authorized.

Building the service

Let’s move on to creating our backend API - we’ll go with Akka HTTP on this one, since it’s arguably the most popular choice out there. As usual, you can checkout the ready-made code from https://github.com/kmikulski/blog-keycloak/tree/master/keycloak-akka-http if you’re in a hurry - otherwise, please follow along.

Let’s start with a Giter8 template for Akka HTTP, to have most of the boilerplate generated for us:

sbt new akka/akka-http-quickstart-scala.g8

The first thing you want to do is add the necessary dependencies to your build.sbt:

We’ll need proper CORS handling for requests coming from our frontend, so let’s add a trait for this:

We can now add it to existing QuickstartServer, making sure that possible authorization errors have proper headers as well:

Finally, let’s put some dummy data into our UserRegistryActor so that we can actually serve something through our service:

First look at the big picture

Let’s stop here for the moment and test if our entire setup works so far. Make sure that the docker container with your Keycloak instance is up (if you’re running a fresh one, you need to define the client and the user as described in the previous post), start the webapp (npm start) and start the backend (sbt run).

As a first step, try querying the API from the initial (unsecured) component level - we’re not enforcing authorization on backend yet, so it should work without a hitch. You can open dev tools to inspect the request:

API request without authorization

Now switch to the secured component (log in using the credentials for the user you created previously), and try doing the same. You should see one major difference in the request headers this time:

API request with authorization

This jumble of characters is the access token that we retrieved from Keycloak earlier in our JSX code. As mentioned before, they’re used by OAuth2-compliant solutions (Keycloak among them) to establish the user’s identity and authorization level. If you’d like to learn more about OAuth2 and different ways that tokens are issued (grant flows), I highly recommend the appropriate section on Aaron Parecki’s website.

So, we now know how to get a token from Keycloak and attach it to a request - how can we now verify on the backend side that this token is indeed valid and not just some random string? Essentially, there are two ways to do this. The obvious one is to simply ask Keycloak about it - we can do it using the token introspection endpoint that Keycloak provides. That is a completely viable option and in fact it’s often used in those OAuth2-based systems that require fine-grained server-side control over tokens (e.g. the possibility to arbitrarily revoke a single token). However, that method requires a separate call to Keycloak every time we want to verify a token (unless we include some caching mechanism on the client side, which would introduce yet another layer of complexity).

In most cases, another method would suit us far better. Keycloak also implements the OpenID protocol, which is an extension atop OAuth2. That means every access token is a JSON Web Token that holds signed, base64-encoded user information in itself. If you look again at the token attached to our request, you’ll be able to see two dots in its body which separate it in three parts:

The first part is the header containing some metadata - you can use any base64-decoding tool to see something akin to this:

The typ field is pretty self-describing, while the alg field denotes the type of algorithm used for signing the token. The value you see above specifically represents RSASSA-PKCS1-v1_5 using SHA-256; there are also other possibilities, which are specified and described in the appropriate RFC. The last field, kid, stands for key ID - since there may be multiple keys in use by the server, that’s what we’ll later use to identify the correct one.

After the header comes the second part, which is the actual payload containing client and user data:

The fields contained herein are referred to by the JWT specification as claims; they represent statements about the given entity. You can look up the meanings of the predefined ones in the IANA JWT Registry.

The last field is the signature which ensures that the other two parts are not tampered with. We’ll use it later to provide actual verification of the token.

Verifying the token

Now that we know how the tokens are constructed, we can try to decompose and verify them. Let’s create a skeleton of the trait that’s going to take care of that (note: I’ll be putting entire logic into a single file to avoid unnecessary obfuscation; in a real-life project, you’ll probably want to preserve separation of concerns by extracting some of this code to different traits/classes):

The first step here will be the authorize directive, which we’ll later use to secure the appropriate route. We start by calling the extractCredentials directive, which attempts to retrieve a token from the request’s Authorization header. If it finds one, we try to verify it and convert it to an instance of Keycloak’s AccessToken, which will be the value extracted by the directive. If verification fails or token is missing, we reject the current route.

We have previously seen that our token has been signed with the RSA algorithm. As you’re probably aware, RSA is an asymmetric system, so in order to verify the signature we’ll need a public key. Where do we get it from? That’s easy - from Keycloak itself. First, we’ll need to let our service know how it can connect to Keycloak server. We do it with the same keycloak.json file that we used last time for our frontend app - if you recall, it should be looking akin to this:

Save that file into the src/main/resources directory, and initialize the configuration in AuthorizationHandler with the following:

KeycloakDeployment class has a method called getJwksUrl, which returns the URL for the endpoint that serves a collection of public keys. In our case the URL will probably be http://localhost:8080/auth/realms/MyDemo/protocol/openid-connect/certs, and opening it in your browser will yield something like this:

Armed with this information, we can retrieve a map of public keys like this:

Please note that we’re persisting the keys as a val here, since it is our intention to avoid having to call Keycloak on every token verification. However, depending on the exact nature of the system you’re creating, it’s conceivable that you will have to take into account the possibility of the keys changing while your service is running; hence, you may want to implement some sort of periodic update scheme here.

We’re almost there - the only thing that remains now is to implement the previously missing verifyToken function:

First, we create an instance of RSATokenVerifier, which is a helper class provided by Keycloak that will do the grunt work of parsing the token and performing actual verification. We then attempt to look up the public key based on the ID from the token header. If we manage to find one, we pass it to the verifier and call the verify method. One thing worth noting is that the return type of that method is also RSATokenVerifier, which enables the use of chaining; verification failure is signalled by throwing a VerificationException, which will propagate to a failed future - that’s why we had to account for this in our original authorize directive by using onComplete instead of onSuccess.

The authorizing directive is ready - we can now place it in our UserRoutes definition like this:

Testing the results

Now that we’ve completed our implementation, let’s test if this actually works! Restart the backend service and go to the root page of the frontend app. First, try sending the request from the unsecured component - if you recall, we’re not attaching the authorization header in this instance.

API request without authorization

So far so good, just what we’d expect. Now go to the secured component (logging in again if necessary) and try resending the request - voilà, works just as it did before! But can we make sure that simply adding a random authorization header isn’t enough to gain access to the service? We’ll try to check that now, and we’re going to use cURL for this. Open the Network tab of your browser’s Developer Tools and locate the request to the users endpoint (you’ll probably see two of them - the first one is the preflight OPTIONS request for CORS, we’re interested in the second one) - if you’re not using some ancient browser, you should have the option to copy the request as cURL. Here’s the result I got, together with the response:

Now try deleting or replacing any part of the token from the request - you should be denied access again:

Where to go from here

You have seen how to provide single sign-on scheme with Keycloak for both the frontend webapp and the backend service, and you have learned a bit about how OAuth2 and OpenID protocols work in practice. Keep in mind that this is just the tip of the iceberg, and there’s a lot more you will want to learn before you decide to employ this solution in a commercial application. Some good sources for what to study next may include the following:

You like this post? Want to stay updated? Follow us on Twitter or subscribe to our Feed.