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

# Secure Cowboy APIs with API Key Authentication

Secure your Cowboy API using a shared secret.

## How Zuplo Handles It

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

## Cowboy Backend Code

```erlang
-module(my_cowboy_handler).
-behaviour(cowboy_router).

-export([init/2, handle_request/1, validate_shared_secret/2]).

init(Req, Opts) ->
    Secret = os:getenv("SHARED_SECRET"),
    {ok, Req, State = #{expected_secret => Secret, options => Opts}}.

handle_request(Req) ->
    State = cowboy_req:state(Req),
    case validate_shared_secret(Req, State) of
        ok ->
            cowboy_req:reply(200, #{<<"content-type">> => <<"application/json">>}, <<"{\"message\": \"Access granted\"}">>, Req);
        {error, Reason} ->
            ErrorMsg = case Reason of
                no_secret_provided -> <<"No secret provided">>;
                invalid_secret -> <<"Invalid secret">>;
                server_configuration_error -> <<"Server configuration error">>
            end,
            cowboy_req:reply(401, #{<<"content-type">> => <<"application/json">>}, <<"{\"error\": \">>, ErrorMsg/binary, <<"\"}">>, Req)
    end.

validate_shared_secret(Req, State) ->
    ExpectedSecret = maps:get(expected_secret, State, undefined),
    case ExpectedSecret of
        undefined ->
            {error, server_configuration_error};
        _ ->
            case cowboy_req:header(<<"x-shared-secret">>, Req, undefined) of
                undefined ->
                    {error, no_secret_provided};
                Secret when length(Secret) =:= length(ExpectedSecret) andalso
                            crypto:mac(:hmac, <<>>, Secret, ExpectedSecret) =:=
                            crypto:mac(:hmac, <<>>, ExpectedSecret, ExpectedSecret) ->
                    ok;
                _ ->
                    {error, invalid_secret}
            end
    end.

% Configure and start the Cowboy server
start() ->
    Dispatch = cowboy_router:compile([
        {'_', [{"/protected", ?MODULE, []}]}
    ]),
    cowboy:start_clear(listener, 100,
        #{env => #{dispatch => Dispatch}}
    ).
```

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