---
title: "Add JWT Authentication to Your Actix Web API"
description: "Secure your Actix Web API using JWT authentication with JWKS."
canonicalUrl: "https://zuplo.com/use-cases/api-key-auth/rust/actixweb/jwt-backend"
framework: "Actix Web"
language: "Rust"
authStrategy: "JWT with JWKS"
pageType: use-case
---

# Add JWT Authentication to Your Actix Web API

Secure your Actix Web API using JWT authentication with JWKS.

## How Zuplo Handles It

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

## Actix Web Backend Code

```rust
use actix_web::{web, App, HttpServer, HttpResponse, Error, HttpRequest};
use actix_web_httpauth::extractors::bearer::BearerAuth;
use jsonwebtoken::{decode, decode_header, Algorithm, DecodingKey, Validation};
use serde::{Deserialize, Serialize};
use reqwest::{Client, Url};
use std::collections::HashMap;

const ISSUER: &str = "https://my-api-a32f34.zuplo.api/__zuplo/issuer";

#[derive(Debug, Deserialize, Serialize)]
struct Claims {
    sub: String,
    // Add necessary fields from JWT claims
}

async fn get_jwks() -> Result<HashMap<String, DecodingKey<'static>>, Error> {
    let jwks_uri = format!("{}/.well-known/jwks.json", ISSUER);
    let response = Client::new().get(jwks_uri).send().await.map_err(Error::from)?;
    let jwks_json: serde_json::Value = response.json().await.map_err(Error::from)?;

    let keys = jwks_json["keys"]
        .as_array()
        .ok_or_else(|| actix_web::error::ErrorInternalServerError("Invalid JWKS response"))?;

    let mut keys_map = HashMap::new();
    for key in keys {
        if let (Some(kid), Some(n), Some(e)) = (
            key["kid"].as_str(),
            key["n"].as_str(),
            key["e"].as_str(),
        ) {
            let decoding_key = DecodingKey::from_rsa_components(n, e).into_static();
            keys_map.insert(kid.to_string(), decoding_key);
        }
    }
    Ok(keys_map)
}

async fn validate_jwt(req: HttpRequest, auth: BearerAuth) -> Result<HttpResponse, Error> {
    let token = auth.token();
    let header = decode_header(token).map_err(|_| actix_web::error::ErrorUnauthorized("Invalid token header"))?;
    let kid = header.kid.ok_or_else(|| actix_web::error::ErrorUnauthorized("Missing kid"))?;

    let keys_map = get_jwks().await?;
    let decoding_key = keys_map.get(&kid).ok_or_else(|| actix_web::error::ErrorUnauthorized("Invalid kid"))?;

    let mut validation = Validation::new(Algorithm::RS256);
    validation.set_issuer(&[ISSUER]);

    let token_data = decode::<Claims>(token, decoding_key, &validation)
        .map_err(|_| actix_web::error::ErrorUnauthorized("Token is invalid"))?;

    Ok(HttpResponse::Ok().json(token_data.claims))
}

async fn protected_route(req: HttpRequest, auth: BearerAuth) -> Result<HttpResponse, Error> {
    validate_jwt(req, auth).await
}

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    HttpServer::new(|| {
        App::new()
            .route("/protected", web::get().to(protected_route))
    })
    .bind("127.0.0.1:8080")?
    .run()
    .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)
