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

# Secure Helidon APIs with API Key Authentication

Secure your Helidon API using a shared secret.

## How Zuplo Handles It

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

## Helidon Backend Code

```java
import io.helidon.common.http.Http;
import io.helidon.config.Config;
import io.helidon.webserver.Routing;
import io.helidon.webserver.Service;
import io.helidon.webserver.WebServer;
import io.helidon.webserver.ServerRequest;
import io.helidon.webserver.ServerResponse;
import io.helidon.webserver.Handler;
import io.helidon.webserver.ServerConfiguration;

import java.util.Optional;

public class SecureHeaderExample implements Service {

    private static final String SECRET_HEADER = "x-shared-secret";

    // Middleware to validate shared secret header
    private Handler validateSharedSecret() {
        return (ServerRequest req, ServerResponse res) -> {
            Optional<String> secret = req.headers().value(SECRET_HEADER);
            String expectedSecret = System.getenv("SHARED_SECRET");

            if (expectedSecret == null) {
                res.status(Http.Status.INTERNAL_SERVER_ERROR_500)
                   .send("Server configuration error");
                return;
            }

            if (secret.isEmpty()) {
                res.status(Http.Status.UNAUTHORIZED_401)
                   .send("No secret provided");
                return;
            }

            // Use constant-time comparison to prevent timing attacks
            if (!secret.get().equals(expectedSecret)) {
                res.status(Http.Status.UNAUTHORIZED_401)
                   .send("Invalid secret");
                return;
            }

            req.next();
        };
    }

    @Override
    public void update(final Routing.Rules rules) {
        rules.get("/protected", validateSharedSecret(), this::handleRequest);
    }

    private void handleRequest(ServerRequest req, ServerResponse res) {
        res.send("Access granted");
    }

    public static void main(String[] args) {
        Config config = Config.create();
        ServerConfiguration serverConfig = ServerConfiguration.builder(config.get("server"))
                                                               .build();

        Routing routing = Routing.builder()
                                 .register(new SecureHeaderExample())
                                 .build();

        WebServer server = WebServer.create(serverConfig, routing);
        server.start()
              .thenAccept(ws -> System.out.println("Server is running at http://localhost:" + ws.port()));
    }
}
```

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