1. About

1.1. Summary

This project shows how Keycloak and Dropwizard can be used together. At the time I wrote this there was no open source integration of the two, so I set up this project.

To read the latest version of this tutorial, please visit: https://ahus1.github.io/keycloak-dropwizard-integration/tutorial.html.

Keycloak provides a standalone OAuth 2.0 and Open ID Connect server. It handles user credentials for your application, so you can focus on business requirements.

Dropwizard is a Java framework for developing ops-friendly, high-performance, REST-ful web services.

The versions 1.0.x and 2.0.x of this project showed alternative ways to integrate Keycloak’s client libraries with Dropwizard versions 1.x to 3.x. With Dropwizard version 4.x, Dropwizard upgraded from Java EE to Jakarta EE. As this would have required additional migration efforts which an unclear added value to users, those have been removed. They might be added in the future if there is co

1.2. How to use

The module keycloak-dropwizard is a ready-to-use Dropwizard module. The releases are available from Maven central.

The releases depend on a version of Dropwizard and Keycloak that was current at release time. To use a more recent release, please add them as an explicit dependency to your project, as this project will not release new versions on every minor or patch release of its dependencies.

Maven Central Build Status

  • Version 0.7.x is tested with Keycloak 1.9.x and Dropwizard 0.9.x

  • Version 0.8.x is tested with Keycloak 2.x.x and Dropwizard 0.9.x

  • Version 0.9.x is tested with Keycloak 2.x.x/3.x.x and Dropwizard 1.0.x

  • Version 1.0.x is tested with Keycloak 3.x.x and Dropwizard 1.1.x/1.2.x/1.3.x

  • Version 1.1.x/1.2.x is tested with Keycloak 4.x-21.x and Dropwizard 1.3.x/2.0.x/2.1.x

  • Version 2.x is tested with Keycloak 4.x-23.x and Dropwizard 3.0.x

  • Version 3.x is tested with Keycloak 23.x and Dropwizard 4.0.x

Starting with Dropwizard 2.0 and the included version of Jersey, a login performed during a POST for a form will not recover the contents of the POST. This is wired into Keycloak’s JettyAdapterSessionStore (that restores the content type and the parameters to the request), but Jersey’s InboundMessageContext that wants to read the information in the request’s header and the body. See the shouldLoginFromPost() test case for an example.

pom.xml
<dependencies>
  <dependency>
    <groupId>de.ahus1.keycloak.dropwizard</groupId>
    <artifactId>keycloak-dropwizard</artifactId>
    <version>x.x.x</version>
  </dependency>
</dependencies>

The most recent development version (based on the master branch on GitHub) is available from the Sonatype OSS Snapshot Repository. To use it, include the following repository in your pom.xml.

pom.xml
<repositories>
    <repository>
        <id>snapshots-repo</id>
        <url>https://oss.sonatype.org/content/repositories/snapshots</url>
        <releases><enabled>false</enabled></releases>
        <snapshots><enabled>true</enabled></snapshots>
    </repository>
</repositories>

1.3. Prerequisites

These examples need a local Keycloak instance with Realm test and user demo with password demo.

Download the Keycloak distribution matching your keycloak-dropwizard-integration version from http://keycloak.org and extract it to a subfolder keycloak-server of this directory. Then call keycloak-server.bat to import an already configured realm. Using this startup file the configuration will be reset every time you start Keycloak.

1.4. Parts

These examples will guide you through setting up Dropwizard and Keycloak in several configurations:

1.5. License

Copyright 2015-2023 Alexander Schwartz and the individual contributors.

Licensed under the Apache License, Version 2.0 (the “License”); you may not use this file except in compliance with the License. You may obtain a copy of the License at

Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.

2. Preparing a Dropwizard Bundle for Keycloak

2.1. What you can see here

This is a Dropwizard bundle that provides a ready-to-use Keycloak integration.

2.2. Step by Step

This shows the main integration points

  1. Setup keycloak as an Authenticator to handle pre-auth integration:

    KeycloakBundle.java
    KeycloakJettyAuthenticator keycloak = new KeycloakDropwizardAuthenticator();
    keycloak.setAdapterConfig(getKeycloakConfiguration(configuration));
    ConstraintSecurityHandler securityHandler = new ConstraintSecurityHandler();
    environment.getApplicationContext().setSecurityHandler(securityHandler);
    environment.getApplicationContext().getSecurityHandler().setAuthenticator(keycloak);
  2. Setup the authentication factory

    KeycloakBundle.java
    environment.jersey().register(new AuthDynamicFeature(
            createAuthFactory(configuration)));
    // To use @RolesAllowed annotations
    environment.jersey().register(RolesAllowedDynamicFeature.class);
    // To use @Auth to inject a custom Principal type into your resource
    environment.jersey().register(new AuthValueFactoryProvider.Binder<>(getUserClass()));

2.3. How to use it

To use it:

  1. register the KeycloakBundle

  2. override getKeycloakConfiguration() to provide the configuration information.

Optionally (if you want to use a different class instead of User to wrap access to Keycloak):

  1. override getUserClass() to return the class you are using

  2. override createAuthenticator() to provide a factory to create the instances of your user class

  3. override createAuthorizer() to implement the role checking used by the @RolesAllowed annotation.

See the default implementations User, KeycloakAuthenticator, and UserAuthorizer as a guide.

Please see the next chapter how to integrate.

3. Dropwizard integration with session-based JAX-RS

3.1. What you can see here

This provides a JAX-RS application with server managed session. It uses Dropwizard’s @Auth annotation.

3.2. Setup for Keycloak

The following elements add Keycloak authentication to Dropwizard and are identical to the simple setup:

  1. Add Keycloak information to config.yml

    config.yml
    server:
      applicationConnectors:
          - type: http
            port: 9090
      adminConnectors:
          - type: http
            port: 9091
    
    keycloakConfiguration:
      realm: test
      auth-server-url: http://localhost:8080
      ssl-required: none
      register-node-at-startup: true
      register-node-period: 600
      resource: test
      credentials:
        secret: 7abd7d08-b10f-4513-bbba-aebebddabb45
  2. Add Keycloak as a security constraint to LotteryConfiguration.java, but without the role and URL mappings.

    LotteryConfiguration.java
    public class LotteryConfiguration extends Configuration {
    
        private KeycloakConfiguration keycloakConfiguration = new KeycloakConfiguration();
    
        public KeycloakConfiguration getKeycloakConfiguration() {
            return keycloakConfiguration;
        }
    
        public void setKeycloakConfiguration(KeycloakConfiguration keycloakConfiguration) {
            this.keycloakConfiguration = keycloakConfiguration;
        }
    }
  3. Add the Keycloak bundle:

    LotteryApplication.java
    bootstrap.addBundle(new KeycloakBundle<LotteryConfiguration>() {
        @Override
        protected KeycloakConfiguration getKeycloakConfiguration(LotteryConfiguration configuration) {
            return configuration.getKeycloakConfiguration();
        }
        /* OPTIONAL: override getUserClass(), createAuthorizer() and createAuthenticator() if you want to use
         * a class other than de.ahus1.keycloak.dropwizard.User to be injected by @Auth */
    });

Once this is set up, Dropwizard’s @Auth annotation can be used as usual in resources:

DrawRessource
@Path("/")
@Produces(MediaType.TEXT_HTML)
public class DrawRessource {

    @Context
    private HttpServletRequest request;

    @POST
    @Path("/draw")
    @RolesAllowed("user")
    public DrawView draw(@FormParam("date") String dateAsString, @Auth User auth) { (1)
        DrawBean bean = new DrawBean();
        LocalDate date = LocalDate.parse(dateAsString);
        bean.setDraw(DrawingService.drawNumbers(date));
        DrawView view = new DrawView(bean);
        bean.setName(auth.getName());
        return view;
    }

    @GET
    @Path("/logout")
    public LogoutView logout(@Context SecurityContext context) throws ServletException { (2)
        if (context.getUserPrincipal() != null) {
            request.logout();
        }
        return new LogoutView();
    }

}
1 This enforces authentication of the user, possibly triggering a full OAuth redirect flow
2 Access the security context directly to find out if the user is logged in without forcing authentication

3.3. How to run

Use the following command line to start it from the parent’s directory

mvn test -pl keycloak-dropwizard-jaxrs-example -am -Pkeycloak-dropwizard-jaxrs-example

Once it is started, point your browser to http://localhost:9090 to see the application.

Enter a date like 2015-01-01 to see the predicted results of the given date.

4. Dropwizard integration with bearer tokens

4.1. What you can see here

This is a setup that includes a small JavaScript client that interfaces with a REST backend. The client authenticates all requests using a Bearer Token.

4.2. Setup for Keycloak

The following elements add Keycloak authentication to Dropwizard and are identical to the simple setup:

  1. Add Keycloak config-bearer.yml with bearer only authentication. This way the Keycloak will not initiate the OAuth redirecting flow. The Keycloak Dropwizard module does not interfere with any OAuth redirect initiated by the frontend.

    config-bearer.yml
    keycloakConfiguration:
      bearer-only: true
  2. Add Keycloak as a security constraint to LotteryConfiguration.java, but without the role and URL mappings.

    LotteryConfiguration.java
    public class LotteryConfiguration extends Configuration {
    
        private KeycloakConfiguration keycloakConfiguration = new KeycloakConfiguration();
    
        public KeycloakConfiguration getKeycloakConfiguration() {
            return keycloakConfiguration;
        }
    
        public void setKeycloakConfiguration(KeycloakConfiguration keycloakConfiguration) {
            this.keycloakConfiguration = keycloakConfiguration;
        }
    }
  3. Add Keycloak as a filter to the REST stack.

    Be aware that all calls to REST resources now require a Bearer Token only if

    • the method or class is annotated with @RolesAllowed OR

    • has a method parameter annotated with @Auth.

    Methods and classes that have neither will proceed when the user hasn’t sent a Bearer Token or is not authenticated, or if the user has sent an (optional) valid Bearer Token. To check if the user has sent an (optional) valid token, add a parameter @Context SecurityContext context and check the logged-in user via context.getUserPrincipal().

    LotteryApplication.java
    bootstrap.addBundle(new KeycloakBundle<>() {
        @Override
        protected KeycloakConfiguration getKeycloakConfiguration(LotteryConfiguration configuration) {
            return configuration.getKeycloakConfiguration();
        }
        /* OPTIONAL: override getUserClass(), createAuthorizer() and createAuthenticator() if you want to use
         * a class other than de.ahus1.keycloak.dropwizard.User to be injected by @Auth */
    });

A simple JavaScript client is located in src/main/resources/assets/ajax.

4.3. How to run

Use the following command line to start it from the parent’s directory

mvn test -pl keycloak-dropwizard-bearermodule -am -Pkeycloak-dropwizard-bearermodule

Once it is started, point your browser to http://localhost:9090/ajax/index.html to see the application.

Enter a date like 2015-01-01 to see the predicted results of the given date.