Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Secure Your Dropwizard Server With Single Sign-on and User Management

DZone's Guide to

Secure Your Dropwizard Server With Single Sign-on and User Management

How to secure your Dropwizard Server with a single sign-on using Okta and OAuth 2.0.

· Java Zone ·
Free Resource

Verify, standardize, and correct the Big 4 + more– name, email, phone and global addresses – try our Data Quality APIs now at Melissa Developer Portal!

We all know that authentication is no fun, especially when you have to build in-house and rolling your own can lead to security and scale issues in the future. With Okta, you can add secure authentication to your Dropwizard site in a matter of minutes. Dropwizard is recognized as the pioneer in turn-key Java API frameworks. Okta also rivals Spring Boot for its ease of adoption. By combining Dropwizard’s production-ready essential libraries and Okta’s identity platform, you can construct a fully secured internet-facing web service with little effort. Read on to see how!

This tutorial assumes familiarity with Java, Maven, and basic web security concepts. The first section sets up a new Dropwizard server from scratch. So, if you already have one up and running, feel free to skip straight to how to integrate with Okta. You can also find the completed code example on GitHub.

Dropwizard Versus Spring Boot

A number of excellent articles and blog posts — notably Takipi and Schibsted — provide thorough comparisons of the two frameworks on both a feature-set level and from an architectural perspective. While Spring Boot has been eclipsing Dropwizard lately in regards to popularity, Dropwizard still provides a compelling out-of-the-box distribution.

Most of the differences boil down to what is included by default versus what is offered as an add-on library. With no other setup needed, Dropwizard gives you exhaustive API metrics, logging, and a handful of useful libraries and tools, such as Jackson, Liquibase, Hibernate, and a few page-templating frameworks. Spring Boot requires that you specify most of these extras, which adds a little more thought, planning, and effort to new server creation. The benefit, however, is greater flexibility and a wider variety of options, such as multiple HTTP server alternatives and less coupling to specific libraries.

Both frameworks provide a well-integrated, mature, production-ready insta-server. The choice between the two usually falls according to the preference of one system’s libraries over the other. If you need the Swiss Army knife or prefer to leave more options open, Spring Boot may be the way to go. If you’re a fan of Jetty, Hibernate, Jersey, et al. and just want to start coding immediately, Dropwizard is hard to beat.

Generate a New Dropwizard Server

First things first, you’ll need a running server. The Dropwizard Maven archetype is a convenient way to create a new project. You can execute the following command to start in interactive mode:

mvn archetype:generate \
    -DarchetypeGroupId=io.dropwizard.archetypes \
    -DarchetypeArtifactId=java-simple


When prompted for various project names, this example used com.example for the groupIddemo for the artifactId, and Demo for the name. The rest were given default values.

Maven archetype output

Almost like a TODO list, Dropwizard outlines the fundamental components of your server by creating a bunch of empty directories. Most of those can be left alone for now.

Directory structure

To start penciling in the new server, create a HomePageResource.java class in the com.example.resources package. This will serve as the “Hello world” entry point for testing and can be enhanced later on with one of Dropwizard’s built-in HTML templating libraries. Two key annotations are needed: one @Path("/") annotation at the class level, indicating that this resource will handle requests to your server’s root URI, and one JAX-RS @GET annotation applied to a simple function that returns a test string.

@Path("/")
public class HomePageResource {
    @GET
    public String handleGetRequest(){
        return "Hello from Dropwizard!";
    }
}


Now, back in com.example.DemoApplication, register this new resource with Jersey in the provided run() method:

@Override
public void run(final DemoConfiguration configuration,
                final Environment environment) {
    environment.jersey().register(new HomePageResource());
}


With that, it’s time to give the server a quick run and make sure all is working as expected. The following two commands will build and start the server on its default port of 8080:

mvn package
java -jar target/demo-1.0-SNAPSHOT.jar server


Once it’s running, visit http://localhost:8080 in your browser and this should relay your "Hello, world" message:

Basic hello world response

Before getting too fancy with the UI, now is a great opportunity to enable single sign-on for your server. The next section will walk you through the process!


Integrate With Okta for OAuth 2.0

Since one of Dropwizard’s goals is to make it easy to create RESTful applications, it provides support for creating an OAuth 2.0 resource server. However, the actual implementation is just a stub and requires you to implement the actual handling of the access token. Okta has created an access token validation library (okta-jwt-verifier) to make it easy to plug this logic into any application.

Create an Okta Account and Gather Credentials

If you don’t already have a free Okta account, you can follow these instructions to create one and set up your first Okta application. There are four important values you will want to take note of:

Use Dropwizard Configuration to Store Your OAuth Settings

Dropwizard’s configuration mechanism is quite easy to work with. All that’s needed is a YAML file with some config values defined and a matching POJO to access the values at runtime (in this case, that POJO is the DemoConfiguration class). You should already have a config.yml in the root of the example directory. Create a new oktaOAuth section and add your OAuth connection details as follows:

oktaOAuth:
  baseUrl: https://dev-820448.oktapreview.com
  issuer: "https://dev-820448.oktapreview.com/oauth2/default"
  clientId: "{yourClientId}"
  audience: "{yourAudience}"  # defaults to ‘api://default’


To cut down on boilerplate, this example just adds these fields as publicly accessible members. You may prefer adding getters and setters.

Create a new class com.example.models.OktaOAuthConfig:

public class OktaOAuthConfig {
    public String baseUrl;
    public String clientId;
    public String issuer;
    public String audience;
}


Now, add our new model to the com.example.DemoConfiguration class.

public class DemoConfiguration extends Configuration {
    public OktaOAuthConfig oktaOAuth = new OktaOAuthConfig();
}


Now, these config values can be easily retrieved in the DemoApplication class via its inherited configuration member.

Handle the OAuth 2.0 Access Token

As I mentioned above, Dropwizard’s OAuth support still requires you to handle the access token yourself. No worries! You can do that in a few lines of code with the Okta JWT Verifier.

First up, add the dropwizard-auth and okta-jwt-verifier dependencies to your pom.xml:

<dependency>
    <groupId>io.dropwizard</groupId>
    <artifactId>dropwizard-auth</artifactId>
</dependency>
<dependency>
    <groupId>com.okta.jwt</groupId>
    <artifactId>okta-jwt-verifier</artifactId>
    <version>0.3.0</version>
</dependency>


Create a Principal Implementation

Next up, I need to create a class to hold the user’s information. Dropwizard expects class to implement java.security.Principal. Create a new class com.example.auth.AccessTokenPrincipal:

public class AccessTokenPrincipal implements Principal {

    private final Jwt accessToken;

    AccessTokenPrincipal(Jwt accessToken) {
        this.accessToken = accessToken;
    }

    @Override
    public String getName() {
        // the 'sub' claim in the access token will be the email address
        return (String) accessToken.getClaims().get("sub");
    }
}


The above class basically just wraps an com.okta.jwt.Jwt and exposes it as a Principal and uses the email address in the sub claim for the name.

Dropwizard Authentication

So far so good. Next, create a new class com.example.auth.OktaOAuthAuthenticator. This is where the magic happens! This class will implement io.dropwizard.auth.Authenticator and validate the access token:

public class OktaOAuthAuthenticator implements Authenticator<String, AccessTokenPrincipal> {

    private final JwtVerifier jwtVerifier;

    public OktaOAuthAuthenticator(JwtVerifier jwtVerifier) {
        this.jwtVerifier = jwtVerifier;
    }

    @Override
    public Optional<AccessTokenPrincipal> authenticate(String accessToken) throws AuthenticationException {

        try {
            Jwt jwt = jwtVerifier.decodeAccessToken(accessToken);
            // if we made it this far we have a valid jwt
            return Optional.of(new AccessTokenPrincipal(jwt));
        } catch (JoseException e) {
            throw new AuthenticationException(e);
        }
    }
}


That is it! Basically, it will consist of two lines of code — one to validate the token and another to return our custom principal type!

Wire it up!

The last step is to wire this all up in our application class, edit DemoApplication , and create a new method configureOAuth():

private void configureOAuth(final DemoConfiguration configuration, final Environment environment) {
    try {
        OktaOAuthConfig widgetConfig = configuration.oktaOAuth;
        // Configure the JWT Validator, it will validate Okta's JWT access tokens
        JwtHelper helper = new JwtHelper()
                .setIssuerUrl(widgetConfig.issuer)
                .setClientId(widgetConfig.clientId);

        // set the audience only if set, otherwise the default is: api://default
        String audience = widgetConfig.audience;
        if (StringUtils.isNotEmpty(audience)) {
            helper.setAudience(audience);
        }

        // register the OktaOAuthAuthenticator
        environment.jersey().register(new AuthDynamicFeature(
            new OAuthCredentialAuthFilter.Builder<AccessTokenPrincipal>()
                .setAuthenticator(new OktaOAuthAuthenticator(helper.build()))
                .setPrefix("Bearer")
                .buildAuthFilter()));

        // Bind our custom principal to the @Auth annotation
        environment.jersey().register(new AuthValueFactoryProvider.Binder<>(AccessTokenPrincipal.class));
    } catch (Exception e) {
        throw new IllegalStateException("Failed to configure JwtVerifier", e);
    }
}


This method does a couple of things. It creates a JwtVerifier based on the properties in our configuration file, registers the new OktaOAuthAuthenticator class, and finally binds the @Auth annotation to our new AccessTokenPrincipal class.

Don’t forget to update the run() method with a call to our new configureOAuth() method.

@Override
public void run(final DemoConfiguration configuration,
                final Environment environment) {

    // configure OAuth
    configureOAuth(configuration, environment);

    // add resources
    environment.jersey().register(new HomePageResource());
}



Finally, update the HomePageResource to require authentication and add a bit more personalization using the @Authannotation.

@Path("/")
public class HomePageResource {

    @GET
    public String handleGetRequest(@Auth AccessTokenPrincipal tokenPrincipal) {
        return "Hello! We'll be contacting you at: " + tokenPrincipal.getName();
    }
}


You could restart the server and start handling requests! But, you, of course, need to get an access token from somewhere. If you were handling class from another OAuth capable application, you could stop here. However, since this is an example, I’m going to add a simple login page using the Okta Sign-In Widget.

Add the Okta Sign-In Widget

Adding a login page to our RESTful application does create a few concerns. I’m going to do this to simplify the example and at the same time show you a few more cool things with Dropwizard.

Expose the OAuth Configuration via REST

Since there is nothing secret in our OAuth configuration (access tokens to not require a client secret to be validated), we can expose our OktaOAuthConfig with a new JAX-RS resource. Create a new class com.example.resources.LoginWidgetConfigResource:

@Path("/signInConfig")
@Produces("application/json")
public class LoginWidgetConfigResource {

    private final OktaOAuthConfig config;

    public LoginWidgetConfigResource(OktaOAuthConfig config) {
        this.config = config;
    }

    @GET
    public OktaOAuthConfig getConfig() {
        return config;
    }
}


It's pretty simple and it's just a getter with a @GET annotation! Back in our DemoApplication class, you need to register the new resource in the run method:

environment.jersey().register(new LoginWidgetConfigResource(configuration.oktaOAuth));


Add a Login Page

Before creating a login page, I need to configure Dropwizard to serve static assets using the concept of an AssetsBundle. This will require another dependency in your pom.xml:

<dependency>
    <groupId>io.dropwizard</groupId>
    <artifactId>dropwizard-assets</artifactId>
</dependency>


In your DemoApplicaiton class, you can register this bundle in the initialize method:

@Override
public void initialize(final Bootstrap<DemoConfiguration> bootstrap) {
    bootstrap.addBundle(new AssetsBundle("/assets/", "/", "index.html"));
}


This configures the application to serve all of the files in src/main/resources/assets at the root (/) of your application. It also defines index.html as the default welcome file.

This creates a small issue. If you restart your application now it would throw an exception. Both our static assets and our resources are being service from the root context. The easy fix is to serve your API resources at /api/* with a single line in your application’s run method, the whole method should now look like:

@Override
public void run(final DemoConfiguration configuration,
                final Environment environment) {

    // base url for our resources
    environment.jersey().setUrlPattern("/api/*");

    // configure OAuth
    configureOAuth(configuration, environment);

    // add resources
    environment.jersey().register(new HomePageResource());
    environment.jersey().register(new LoginWidgetConfigResource(configuration.oktaOAuth));
}


The only thing left to do is to create a login page. I’m actually going to create a simple SPA app with a single index.html file. This page will load the widget configuration from /api/signInConfig, prompt the user to login, and then display the results from a call to /api/message. I’m not going to dig into the contents of the HTML. If you are interested, you should be able to follow the comments.

<!doctype html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <title>Dropwizard OAuth 2.0 Example</title>
    <base href="/">

    <script src="https://ok1static.oktacdn.com/assets/js/sdk/okta-signin-widget/2.6.0/js/okta-sign-in.min.js" type="text/javascript"></script>
    <link href="https://ok1static.oktacdn.com/assets/js/sdk/okta-signin-widget/2.6.0/css/okta-sign-in.min.css" type="text/css" rel="stylesheet">
    <link href="https://ok1static.oktacdn.com/assets/js/sdk/okta-signin-widget/2.6.0/css/okta-theme.css" type="text/css" rel="stylesheet">

    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js" type="text/javascript"></script>
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" type="text/javascript"></script>
</head>

<body>
<!-- Render the login widget here -->
<div id="okta-login-container"></div>

<div class="container">
    <!-- Render the REST response here -->
    <div id="api-message"></div>

    <!-- And a logout button, hidden by default -->
    <button id="logout" type="button" class="btn btn-danger" style="display:none">Logout</button>
</div>
<script>
    $.ajax({
      url: "/api/signInConfig",
    }).then(function(data) {

      // we are priming our config object with data retrieved from the server in order to make this example easier to run
      // You could statically define your config like if you wanted too:
      /*
      let config = {
        baseUrl: 'https://dev-123456.oktapreview.com',
        clientId: '00icu81200icu812w0h7',
        redirectUri: 'http://localhost:8080',
        authParams: {
          issuer: 'https://dev-123456.oktapreview.com/oauth2/default',
          responseType: ['id_token', 'token']
        }
      }; */

      window.oktaSignIn = new OktaSignIn({
        baseUrl: data.baseUrl,
        clientId: data.clientId,
        redirectUri: window.location.href,
        authParams: {
          issuer: data.issuer,
          responseType: ['id_token', 'token'],
          scopes: ["openid", "profile", "email"]
        }
      });

      // handle the rest of the page
      doInit();
    });

  /**
   * Makes a request to a REST resource and displays a simple message to the page.
   * @param accessToken The access token used for the auth header
   */
  function renderApiMessage(accessToken) {
    // include the Bearer token in the request
    $.ajax({
      url: "/api/message",
      headers: {
        'Authorization': "Bearer " + accessToken
      },
    }).then(function(data) {
      // Render the message of the day
      let htmlToRender = `
            <h1>Message:
              <small>/api/message</small>
            </h1>
            <p>${data}</p>`;

      $('#api-message').append(htmlToRender);
    })
    .fail(function(data) {
      // handle any errors
      $('#api-message').append("ERROR, check your browsers console log!");
      console.log("ERROR!!");
      console.log(data.responseJSON);
    });

    // show the logout button
    $( "#logout" )[0].style.display = 'block';
  }

  function doInit() {

    $( "#logout" ).click(function() {
      oktaSignIn.signOut(() => {
        oktaSignIn.tokenManager.clear();
        location.reload();
      });
    });

    // Check if we already have an access token
    const token = oktaSignIn.tokenManager.get('my_access_token');

    // if we do great, just go with it!
    if (token) {
      renderApiMessage(token.accessToken)
    } else {

      // otherwise show the login widget
      oktaSignIn.renderEl(
        {el: '#okta-login-container'},
        function (response) {

          // check if success
          if (response.status === 'SUCCESS') {

            // for our example we have the id token and the access token
            // oktaSignIn.tokenManager.add('my_id_token', response[0]);
            oktaSignIn.tokenManager.add('my_access_token', response[0]);

            // hide the widget
            oktaSignIn.hide();

            // now for the fun part!
            renderApiMessage(response[1].accessToken);
          }
        },
        function (err) {
          // handle any errors
          console.log(err);
        }
      );
    }
  }
</script>
</body>
</html>


Whew! You’ve emerged from the jungle of hand-rolled OIDC clients and now have authorization in your Dropwizard server! There were quite a few code examples above, so if you need to verify anything you built along the way, you can always access the complete source for this project on GitHub.

Ok… time to see it in action! You can once again build the project with:

mvn clean package


But this time, you’ll also need to specify the location of the config.yml as a command line argument when starting the server. It needs to include the path relative to the current working directory:

java -jar target/demo-1.0-SNAPSHOT.jar server config.ym


By visiting http:/localhost:8080 in your browser, it should now redirect you to sign in on Okta’s domain, and following that, it should present a message with your email address. If so, congratulations! If you’ve had difficulty at any point along the way, try running the example as is.

Post login message showing API response

Extra Credit: Health Checks

You probably have noticed the console warnings when starting your application:

!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!    THIS APPLICATION HAS NO HEALTHCHECKS. THIS MEANS YOU WILL NEVER KNOW      !
!     IF IT DIES IN PRODUCTION, WHICH MEANS YOU WILL NEVER KNOW IF YOU'RE      !
!    LETTING YOUR USERS DOWN. YOU SHOULD ADD A HEALTHCHECK FOR EACH OF YOUR    !
!         APPLICATION'S DEPENDENCIES WHICH FULLY (BUT LIGHTLY) TESTS IT.       !
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

Dropwizard makes it really easy to add existing health checks or to create your own. I’ll leave that as an exercise for you!

Learn More

In this post, I’ve created a self-contained Dropwizard application with a couple JAX-RS resources and a simple HTML page. Take a look at Dropwizard’s getting started guide or these resources for more info.

Developers! Quickly and easily gain access to the tools and information you need! Explore, test and combine our data quality APIs at Melissa Developer Portal – home to tools that save time and boost revenue. Our APIs verify, standardize, and correct the Big 4 + more – name, email, phone and global addresses – to ensure accurate delivery, prevent blacklisting and identify risks in real-time.

Topics:
java ,dropwizard ,single sign on ,sso ,okta ,spring boot ,api ,oauth 2.0

Published at DZone with permission of

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}