---
title: "Add JWT Authentication to Your undefined API"
description: undefined
canonicalUrl: "https://zuplo.com/use-cases/api-key-auth/rust/warp/jwt-backend"
framework: undefined
language: undefined
authStrategy: "JWT with JWKS"
pageType: use-case
---

# Add JWT Authentication to Your undefined API



## How Zuplo Handles It

Let Zuplo issue short-lived JWTs signed with a JWKS your undefined backend can verify — no long-lived API keys touch your origin.

## undefined Backend Code

```rust
use warp::Filter;
use jsonwebtoken::{decode, decode_header, Algorithm, DecodingKey, Validation};
use serde::{Deserialize, Serialize};
use reqwest::Client;
use std::collections::HashMap;
use std::sync::{Arc, RwLock};

// Constants
const ISSUER: &str = "https://my-api-a32f34.zuplo.api/__zuplo/issuer";
const JWKS_URI: &str = "https://my-api-a32f34.zuplo.api/__zuplo/issuer/.well-known/jwks.json";

// Structs
#[derive(Debug, Serialize, Deserialize)]
struct Claims {
    sub: String,
    // Add more fields as needed
}

#[derive(Debug, Deserialize)]
struct Jwk {
    kid: String,
    n: String,
    e: String,
    alg: String,
    kty: String,
    use: String,
    #[serde(rename = "x5c")]
    x5c: Vec<String>,
}

#[derive(Debug, Deserialize)]
struct Jwks {
    keys: Vec<Jwk>,
}

// JWKS Client setup
async fn fetch_jwks(client: &Client) -> Result<Jwks, reqwest::Error> {
    let resp = client.get(JWKS_URI).send().await?;
    resp.json::<Jwks>().await
}

fn get_signing_key(jwks: &Jwks, kid: &str) -> Option<String> {
    jwks.keys.iter().find(|key| key.kid == kid).map(|key| key.x5c[0].clone())
}

async fn validate_jwt(token: &str, jwks: Arc<RwLock<Jwks>>) -> Result<Claims, warp::Rejection> {
    let header = decode_header(token).map_err(|_| warp::reject::custom("Invalid token header"))?;
    let kid = header.kid.ok_or_else(|| warp::reject::custom("No 'kid' in token"))?;

    let jwks_data = jwks.read().unwrap();
    let x5c = get_signing_key(&*jwks_data, &kid).ok_or_else(|| warp::reject::custom("No matching key found"))?;

    let decoding_key = DecodingKey::from_rsa_pem(x5c.as_bytes()).map_err(|_| warp::reject::custom("Invalid key"))?;
    let validation = Validation::new(Algorithm::RS256);
    let token_data = decode::<Claims>(token, &decoding_key, &validation).map_err(|_| warp::reject::custom("Token decode error"))?;

    Ok(token_data.claims)
}

fn jwt_filter(jwks: Arc<RwLock<Jwks>>) -> impl Filter<Extract = (Claims,), Error = warp::Rejection> + Clone {
    warp::header::<String>("authorization")
        .and_then(move |auth_header: String| {
            let jwks = jwks.clone();
            async move {
                let token = auth_header.trim_start_matches("Bearer ");
                validate_jwt(token, jwks).await
            }
        })
}

#[tokio::main]
async fn main() {
    let client = Client::new();
    let jwks = fetch_jwks(&client).await.expect("Failed to fetch JWKS");
    let jwks = Arc::new(RwLock::new(jwks));

    let protected_route = warp::path("protected")
        .and(jwt_filter(jwks.clone()))
        .map(|claims: Claims| {
            warp::reply::json(&claims)
        });

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

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