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

# Secure Javalin APIs with API Key Authentication

Secure your Javalin API using a shared secret.

## How Zuplo Handles It

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

## Javalin Backend Code

```java
import io.javalin.Javalin;
import io.javalin.http.Context;
import io.javalin.http.Handler;
import io.javalin.http.HttpResponseException;

import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.Optional;

public class SecureApiExample {

    public static void main(String[] args) {
        String expectedSecret = Optional.ofNullable(System.getenv("SHARED_SECRET"))
                                        .orElseThrow(() -> new RuntimeException("Server configuration error"));

        Javalin app = Javalin.create().start(7000);

        app.before("/protected/*", new SharedSecretValidator(expectedSecret));

        app.get("/protected/endpoint", ctx -> {
            ctx.json(new Message("Access granted"));
        });
    }

    static class SharedSecretValidator implements Handler {

        private final String expectedSecretHash;

        SharedSecretValidator(String expectedSecret) {
            this.expectedSecretHash = hashSecret(expectedSecret);
        }

        @Override
        public void handle(Context ctx) {
            String secret = ctx.header("x-shared-secret");

            if (secret == null) {
                throw new HttpResponseException(401, "No secret provided");
            }

            String providedSecretHash = hashSecret(secret);
            if (!timingSafeEqual(providedSecretHash, expectedSecretHash)) {
                throw new HttpResponseException(401, "Invalid secret");
            }
        }

        private String hashSecret(String secret) {
            try {
                MessageDigest digest = MessageDigest.getInstance("SHA-256");
                byte[] hash = digest.digest(secret.getBytes(StandardCharsets.UTF_8));
                return new String(hash, StandardCharsets.UTF_8);
            } catch (NoSuchAlgorithmException e) {
                throw new RuntimeException("Error hashing secret", e);
            }
        }

        private boolean timingSafeEqual(String a, String b) {
            byte[] bytesA = a.getBytes(StandardCharsets.UTF_8);
            byte[] bytesB = b.getBytes(StandardCharsets.UTF_8);
            return MessageDigest.isEqual(bytesA, bytesB);
        }
    }

    static class Message {
        private String message;

        Message(String message) {
            this.message = message;
        }

        public String getMessage() {
            return message;
        }
    }
}
```

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