Security in Quarkus Applications via Keycloak

I’ve developed a sample that leverages Keycloak to do authentication and authorization in Quarkus applications. In this article I share my experiences and tips.

Get the code from GitHub.

My sample contains a web application which invokes an API microservice and that one invokes a second microservice. To see the results in the web application, users need to be authenticated and they need to have the role ‘user’. Here is the architecture:

The Keycloak documentation describes pretty well how to install Keycloak in OpenShift. The difficult part was the creation of the realm which I’ve documented in my previous article Setting up Keycloak in OpenShift.

Quarkus comes with two great quides that describe how to use Keycloak in web apps and services:

Developing protected Endpoints

The service Articles provides an endpoint ‘/articles’ which only users with the role ‘user’ can access. In application.properties the Keycloak URL is defined as well as the client ID and secret.

quarkus.oidc.auth-server-url=https://keycloak-default.niklas-heidloff-b3c-4x16-162e406f043e20da9b0ef0731954a894-0000.us-south.containers.appdomain.cloud/auth/realms/quarkus
quarkus.oidc.client-id=backend-service
quarkus.oidc.credentials.secret=secret
quarkus.http.port=8082
quarkus.http.cors=true
resteasy.role.based.security=true

Note the line ‘resteasy.role.based.security=true’. This setting is important, so that the Articles service can receive the Authorization header from the Web-API service. I couldn’t find this in the Quarkus documentation, but Phillip Kr├╝ger from the Quarkus team answered it today.

Once you’ve configured your Quarkus application, implementing the endpoint is trivial. I’ve used @RolesAllowed, but there are other annotations available, for example @Authenticated.

@GET
@Path("/articles")
@Produces(MediaType.APPLICATION_JSON)
@RolesAllowed("user")
@NoCache
public Set<Article> getArticles() {  
   return articles;
}

This allows my test user Alice to invoke this endpoint, since she has the role ‘user’.

Invoking protected Endpoints

The Web-API service has also a protected endpoint which has been implemented as above. Additionally it also invokes the Articles service. In order to do this, the MicroProfile REST Client is used.

Let’s take at the configuration first.

quarkus.oidc.auth-server-url=https://keycloak-default.niklas-heidloff-b3c-4x16-162e406f043e20da9b0ef0731954a894-0000.us-south.containers.appdomain.cloud/auth/realms/quarkus
quarkus.oidc.client-id=backend-service
quarkus.oidc.credentials.secret=secret
quarkus.http.port=8081
quarkus.http.cors=true
org.eclipse.microprofile.rest.client.propagateHeaders=Authorization

The last line is important again. This allows forwarding the authorization header with the JWT token without having to implement any code.

private ArticlesService articlesService;

@PostConstruct
void initialize() {
   URI apiV1 = UriBuilder.fromUri("http://{host}:{port}/articles").build(articlesHost, articlesPort);     
   articlesService = RestClientBuilder.newBuilder()
      .baseUri(apiV1)
      .register(ExceptionMapperArticles.class)
      .build(ArticlesService.class);        
}

public List<CoreArticle> getArticles(int amount) throws NoConnectivity {
   try {
      return articlesService.getArticlesFromService(amount);
   } catch (Exception e) {
      throw new NoConnectivity(e);
   }
}

If you want to try this out yourself, get the code. I’ve documented how to use Red Hat OpenShift on the IBM Cloud, but you can also use other Kubernetes distributions or local Docker.