Drop Wizard Firebase

Dropwizard & Firebase

The dropwizard-auth client provides authentication using either HTTP Basic Authentication or OAuth2 bearer tokens. In this tutorial we will show you how to implement OAuth 2.0 authentication in a Dropwizard application with firebase as the authorization server.

Authorization Flow

Let’s consider a scenario involving a Dropwizard server, a mobile application, Firebase service and a user. The following diagram depicts how these components interact:

 

dropwizard-firebase

The mobile device and the user icons are courtesy of Vildana and Jose Moya from Noun Project.

  1. The user signs up using the mobile application
  2. The mobile application would register the user with firebase, sign the user in and receives an access token.
  3. The mobile application includes the access token it receives from firebase in every request it sends to the Dropwizard application.
  4. The dropwizard application validates the access token and if valid, serves the request to protected resources.

In this article we focus on step 4 above. How a Dropwizard application (the resource server) can interact with a Firebase service (authorization server) so it can accept and respond to protected resource requests using access tokens.

Protect Your Resources

In Dropwizard you need to annotate your resource methods with one of the security annotations
@PermitAll,@RollesAllowed or @DenyAll provided in the javax.annotation.security package or alternatively annotate the parameter representing your principal with @Auth as described in the Dropwizard Authentication tutorial
It is important to note that if none of these annotations are used on a resource method, that method would be open to use by anyone by default; that is different from annotating it with @PermitAll which guarantees the user will still be authenticated, but their roles will be ignored. A good practice to ensure all your resources are protected is to write a test that checks appropriate annotations are used on each resource method. See Al Scott’s ResourceAuthTest as an example. Be aware, however that the test does not account for cases where authentication is triggered by using @Auth annotation.

Setup OAuth 2.0 authentication

In OAuth 2.0 authorization framework described in RFC 6749, the authorization server may be the same as the resource server or a separate entity. In our example the authorization server is Firebase and it is separate from our resource server which is a Dropwizard application.

To setup OAuth 2.0 authentication within Dropwizard it is best to follow instructions on Dropwizard Authentication tutorial. The necessary thing to understand is that an AuthFilter needs to be created and registered with jersey. The AuthFilter then will be applied to every request sent to the server before the request is dispatched to a resource method. The filter extracts the access token from a request and asks the authenticator to authenticate the user and return a principal object.

At the time of writing, one could setup OAuth 2.0 authentication in Dropwizard with a few lines of code:

@Override
public void run(ExampleConfiguration configuration,
                Environment environment) {
  environment.jersey().register(new AuthDynamicFeature(
    new OAuthCredentialAuthFilter.Builder<User>()
      .setAuthenticator(new FirebaseOAuthAuthenticator())
      .setPrefix("Bearer")
      .buildAuthFilter()));
  //If you want to use @Auth to inject a custom Principal type into your resource
  environment.jersey().register(new AuthValueFactoryProvider.Binder<>(User.class));
}

The code is borrowed from the dropwizard tutorial but we have left out authorization as we focus on authentication. Now we need to implement a FirebaseOAuthAuthenticator which accepts the access token as a string and returns the authenticated principal. But before being able to implement this class we need to configure our Dropwizard server to use the firebase.

Add Firebase Admin SDK to your project

This java library should be added to your application’s dependencies so you can use firebase from your dropwizard application to validate access tokens sent by the mobile application. You can find the library in maven. If using gradle include the following dependency:

compile "com.google.firebase:firebase-admin:4.1.2"

warning: You might need to exclude guava-jdk5 from firebase-admin transitive dependencies because Dropwizard already imports guava as a transitive dependency and having the old guava-jdk5 can cause issues:

compile('com.google.firebase:firebase-addmin:4.1.2') {
    exclude group: 'com.google.guava', module:'guava-jdk5'
}

Initialize the SDK

The following snippet is copied from firebase documentation:

FileInputStream serviceAccount = new FileInputStream("path/to/serviceAccountKey.json");

FirebaseOptions options = new FirebaseOptions.Builder()
    .setCredential(FirebaseCredentials.fromCertificate(serviceAccount))
    .setDatabaseUrl("https://my-db.firebaseio.com")
    .build();

FirebaseApp.initializeApp(options);

As you can see we need to initialize the firebase SDK based on some options, e.g seviceAccountKey and databaseUrl. A good way to manage related options likes these in Dropwizard is to create a separate configuration class. Let’s call this class FirebaseOptionsFactory:

public class FirebaseOptionsFactory {

   @NotBlank
   @JsonProperty
   private String databaseUrl;

   @NotBlank
   @JsonProperty
   private String credentialPath;

   public String getDatabaseUrl() {
       return databaseUrl;
   }

   public void setDatabaseUrl(String databaseUrl) {
       this.databaseUrl = databaseUrl;
   }

   public String getCredentialPath() {
       return credentialPath;
   }

   public void setCredentialPath(String credentialPath) {
       this.credentialPath = credentialPath;
   }

   public FirebaseOptions build() throws FileNotFoundException {
       FileInputStream serviceAccount = new FileInputStream(credentialPath);
       return new FirebaseOptions.Builder()
               .setCredential(FirebaseCredentials.fromCertificate(serviceAccount))
               .setDatabaseUrl(databaseUrl)
               .build();
   }
}

Your main Configuration subclass can then include this as a member field:

public class MyConfiguration extends Configuration {
   @Valid
   @NotNull
   @JsonProperty("firebase")
   private FirebaseOptionsFactory firebaseOptions;

   public FirebaseOptionsFactory getFirebaseOptionsFactory() {
       return firebaseOptions;
   }

   public void setFirebaseOptions(FirebaseOptionsFactory firebaseOptions) {
       this.firebaseOptions = firebaseOptions;
   }

Then, in your application’s YAML file, you can use a nested firebase field:

firebase:
 databaseUrl: https://my-db.firebaseio.com/
 credentialPath: config/firebase-credentials.json

And finally in your application’s run method you can initialize the firebase SDK like this:

@Override
public void run(MyConfiguration configuration, Environment environment) throws Exception{

   //initialize firebase app
   final FirebaseOptions options = configuration.getFirebaseOptionsFactory().build();
   FirebaseApp.initializeApp(options);
}

Now that the firebase SDK is initialized we are finally ready to implement the FirebaseOAuthAuthenticator.

Validate Access Tokens

As mentioned before the FirebaseOAuthAuthenticator will accept an access token as a string and if the token is valid, it will return the authenticated principal:

public class FirebaseOAuthAuthenticator implements Authenticator<String, Member> {
   @Override
   public Optional<Member> authenticate(String credentials) throws AuthenticationException {
       final User authenticatedUser = null;
       final CountDownLatch done = new CountDownLatch(1);

       FirebaseAuth.getInstance().verifyIdToken(credentials)
               .addOnSuccessListener(firebaseToken -> {
                   authenticatedUser = new User(firebaseToken.getEmail());
                   done.countDown();
               })
               .addOnFailureListener(e -> {
                   done.countDown();
               });

       try {
           done.await();
       } catch (InterruptedException e) {
           throw new AuthenticationException(e.getMessage(), e);
       }
       return Optional.ofNullable(authenticatedUser);
   }
}
Maryam Khezrzadeh

Maryam Khezrzadeh

Senior Software Engineer at A.Y. Technologies
Maryam has an extensive background as a developer and software engineer from companies like Indicee and Dun & Bradstreet. She is our resident Senior Software Engineer and a talented writer.
Maryam Khezrzadeh

Latest posts by Maryam Khezrzadeh (see all)

2 Comments
  1. Sal 2 years ago

    Hi Maryam,
    Is there a github repo for the code shown on this post? I am interested in seeing the Dropwizard OAuth2 authentication full cycle thorough the code.

    Thanks,
    Sal

    • Amin Yazdani 2 years ago

      Hi Sal,

      Unfortunately we don’t have the code shown here in a public git repo. Maryam and I will chat about possibility of adding this to a hello-world app and publishing it in a repository in the future.

Leave a reply

Your email address will not be published. Required fields are marked *

©2019 A.Y.Technologies

Log in with your credentials

Forgot your details?