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

# Secure Warp APIs with API Key Authentication

Secure your Warp API using a shared secret.

## How Zuplo Handles It

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

## Warp Backend Code

```rust
use warp::Filter;
use warp::http::StatusCode;
use warp::reject::custom;
use std::env;
use std::convert::Infallible;
use tokio::sync::oneshot;
use warp::reject::Reject;

#[derive(Debug)]
struct Unauthorized;
impl Reject for Unauthorized {}

fn with_shared_secret(secret: String) -> impl Filter<Extract = (), Error = warp::Rejection> + Clone {
    warp::header::optional("x-shared-secret")
        .and_then(move |provided_secret: Option<String>| {
            let expected_secret = secret.clone();
            async move {
                match provided_secret {
                    Some(provided) if timing_safe_compare(&provided, &expected_secret) => Ok(()),
                    _ => Err(custom(Unauthorized)),
                }
            }
        })
}

fn timing_safe_compare(provided: &str, expected: &str) -> bool {
    if provided.len() != expected.len() {
        return false;
    }
    let mut result = 0;
    for (a, b) in provided.bytes().zip(expected.bytes()) {
        result |= a ^ b;
    }
    result == 0
}

#[tokio::main]
async fn main() {
    dotenv::dotenv().ok();
    let secret = env::var("SHARED_SECRET").expect("SHARED_SECRET must be set");

    let protected_route = warp::path("protected")
        .and(warp::get())
        .and(with_shared_secret(secret))
        .map(|| warp::reply::json(&serde_json::json!({ "message": "Access granted" })));

    let routes = protected_route.recover(handle_rejection);

    warp::serve(routes).run(([127, 0, 0, 1], 3030)).await;
}

async fn handle_rejection(err: warp::Rejection) -> Result<impl warp::Reply, Infallible> {
    if err.find::<Unauthorized>().is_some() {
        Ok(warp::reply::with_status(
            warp::reply::json(&serde_json::json!({ "error": "Invalid secret" })),
            StatusCode::UNAUTHORIZED,
        ))
    } else {
        Ok(warp::reply::with_status(
            warp::reply::json(&serde_json::json!({ "error": "Server error" })),
            StatusCode::INTERNAL_SERVER_ERROR,
        ))
    }
}
```

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