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

# Secure Micronaut APIs with API Key Authentication

Secure your Micronaut API using a shared secret.

## How Zuplo Handles It

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

## Micronaut Backend Code

```java
import io.micronaut.http.HttpRequest;
import io.micronaut.http.HttpResponse;
import io.micronaut.http.annotation.Controller;
import io.micronaut.http.annotation.Get;
import io.micronaut.http.filter.HttpFilter;
import io.micronaut.http.filter.ServerFilterChain;
import jakarta.inject.Singleton;
import org.reactivestreams.Publisher;
import io.reactivex.Flowable;

import javax.annotation.Nullable;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.util.Objects;
import java.util.Optional;
import java.util.Base64;

@Singleton
public class SharedSecretFilter implements HttpFilter {

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

    @Override
    public Publisher<? extends HttpResponse<?>> doFilter(HttpRequest<?> request, ServerFilterChain chain) {
        Optional<String> sharedSecretOpt = request.getHeaders().get(SHARED_SECRET_HEADER, String.class);
        String expectedSecret = System.getenv(SHARED_SECRET_ENV);

        if (expectedSecret == null) {
            return Flowable.just(HttpResponse.serverError().body("Server configuration error"));
        }

        return sharedSecretOpt.map(secret -> {
            if (isValidSecret(secret, expectedSecret)) {
                return chain.proceed(request);
            }
            return Flowable.just(HttpResponse.unauthorized().body("Invalid secret"));
        }).orElse(Flowable.just(HttpResponse.unauthorized().body("No secret provided")));
    }

    private boolean isValidSecret(String secret, String expectedSecret) {
        try {
            MessageDigest messageDigest = MessageDigest.getInstance("SHA-256");
            byte[] secretHash = messageDigest.digest(secret.getBytes(StandardCharsets.UTF_8));
            byte[] expectedSecretHash = messageDigest.digest(expectedSecret.getBytes(StandardCharsets.UTF_8));
            return MessageDigest.isEqual(secretHash, expectedSecretHash);
        } catch (Exception e) {
            return false;
        }
    }
}

@Controller("/protected")
public class ProtectedController {

    @Get
    public HttpResponse<String> index() {
        return HttpResponse.ok("Access granted");
    }
}
```

## 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)
