---
title: "Secure Dropwizard APIs with API Key Authentication"
description: "Secure your Dropwizard API using a shared secret."
canonicalUrl: "https://zuplo.com/use-cases/api-key-auth/java/dropwizard/secure-header"
framework: "Dropwizard"
language: "Java"
authStrategy: "shared secret header"
pageType: use-case
---

# Secure Dropwizard APIs with API Key Authentication

Secure your Dropwizard API using a shared secret.

## How Zuplo Handles It

Put Zuplo in front of your Dropwizard backend to authenticate API keys and forward a shared secret header so your origin only accepts traffic from Zuplo.

## Dropwizard Backend Code

```java
import io.dropwizard.Application;
import io.dropwizard.setup.Bootstrap;
import io.dropwizard.setup.Environment;
import io.dropwizard.jersey.setup.JerseyEnvironment;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerRequestFilter;
import javax.ws.rs.ext.Provider;
import java.io.IOException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;
import java.util.Objects;

// Filter to validate shared secret header
@Provider
public class SharedSecretFilter implements ContainerRequestFilter {

    private static final String SHARED_SECRET_HEADER = "X-Shared-Secret";
    private final String expectedSecret;

    public SharedSecretFilter() {
        this.expectedSecret = System.getenv("SHARED_SECRET");
    }

    @Override
    public void filter(ContainerRequestContext requestContext) throws IOException {
        if (expectedSecret == null) {
            throw new IllegalStateException("Server configuration error: SHARED_SECRET not set");
        }

        String secret = requestContext.getHeaderString(SHARED_SECRET_HEADER);
        if (secret == null) {
            requestContext.abortWith(javax.ws.rs.core.Response.status(401)
                .entity("{\"error\":\"No secret provided\"}")
                .build());
            return;
        }

        try {
            if (!secureCompare(secret, expectedSecret)) {
                requestContext.abortWith(javax.ws.rs.core.Response.status(401)
                    .entity("{\"error\":\"Invalid secret\"}")
                    .build());
            }
        } catch (NoSuchAlgorithmException e) {
            requestContext.abortWith(javax.ws.rs.core.Response.status(500)
                .entity("{\"error\":\"Server error\"}")
                .build());
        }
    }

    private boolean secureCompare(String a, String b) throws NoSuchAlgorithmException {
        byte[] aDigest = MessageDigest.getInstance("SHA-256").digest(a.getBytes());
        byte[] bDigest = MessageDigest.getInstance("SHA-256").digest(b.getBytes());
        return MessageDigest.isEqual(aDigest, bDigest);
    }
}

// Main application class
public class MyApplication extends Application<MyConfiguration> {

    @Override
    public void initialize(Bootstrap<MyConfiguration> bootstrap) {
    }

    @Override
    public void run(MyConfiguration configuration, Environment environment) {
        JerseyEnvironment jersey = environment.jersey();
        jersey.register(new SharedSecretFilter());
        jersey.register(MyResource.class);
    }

    public static void main(String[] args) throws Exception {
        new MyApplication().run(args);
    }
}

// Example resource class
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.core.Response;

@Path("/protected")
public class MyResource {

    @GET
    public Response getProtected() {
        return Response.ok("{\"message\":\"Access granted\"}").build();
    }
}
```

## Example Request

```bash
curl -X GET \
  'https://your-api.zuplo.dev/your-route' \
  -H 'Authorization: Bearer YOUR_API_KEY'
```

## Learn More

- [API Key Authentication on Zuplo](https://zuplo.com/docs/policies/api-key-auth-inbound)
- [JWT Authentication on Zuplo](https://zuplo.com/docs/policies/open-id-jwt-auth-inbound)
- [All use cases](https://zuplo.com/use-cases)
