---
title: "Add JWT Authentication to Your FastAPI API"
description: "Secure your FastAPI API using JWT authentication with JWKS."
canonicalUrl: "https://zuplo.com/use-cases/api-key-auth/python/fastapi/jwt-backend"
framework: "FastAPI"
language: "Python"
authStrategy: "JWT with JWKS"
pageType: use-case
---

# Add JWT Authentication to Your FastAPI API

Secure your FastAPI API using JWT authentication with JWKS.

## How Zuplo Handles It

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

## FastAPI Backend Code

```python
from fastapi import FastAPI, Depends, HTTPException, status
from fastapi.security import HTTPBearer
from jose import jwt
from jose.exceptions import JWTError, ExpiredSignatureError
import requests
from cachetools import TTLCache
from starlette.requests import Request

app = FastAPI()

ISSUER = "https://my-api-a32f34.zuplo.api/__zuplo/issuer"
JWKS_URL = f"{ISSUER}/.well-known/jwks.json"
ALGORITHMS = ["RS256"]

# Cache for JWKS keys
jwks_cache = TTLCache(maxsize=10, ttl=600)

def get_jwks():
    if JWKS_URL not in jwks_cache:
        response = requests.get(JWKS_URL)
        response.raise_for_status()
        jwks_cache[JWKS_URL] = response.json()
    return jwks_cache[JWKS_URL]

def get_key(token):
    unverified_header = jwt.get_unverified_header(token)
    jwks = get_jwks()
    for jwk in jwks['keys']:
        if jwk['kid'] == unverified_header['kid']:
            return jwt.algorithms.RSAAlgorithm.from_jwk(jwk)
    raise JWTError("Unable to find appropriate key")

def validate_jwt(auth: HTTPBearer = Depends()):
    token = auth.credentials
    try:
        key = get_key(token)
        payload = jwt.decode(token, key, algorithms=ALGORITHMS, issuer=ISSUER)
        return payload
    except ExpiredSignatureError:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="Token has expired"
        )
    except JWTError as e:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="Invalid token"
        )

@app.get("/protected")
async def protected_route(user: dict = Depends(validate_jwt)):
    return {"message": "Access granted", "user": user}
```

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