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

# Add JWT Authentication to Your CherryPy API

Secure your CherryPy API using JWT authentication with JWKS.

## How Zuplo Handles It

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

## CherryPy Backend Code

```python
import cherrypy
from jose import jwt
from jose.exceptions import JWTError, ExpiredSignatureError
import requests
from cachetools import TTLCache
import json

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:
        resp = requests.get(JWKS_URL)
        resp.raise_for_status()
        jwks_cache[JWKS_URL] = resp.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 require_jwt():
    auth_header = cherrypy.request.headers.get('Authorization')
    if not auth_header or not auth_header.startswith('Bearer '):
        cherrypy.response.status = 401
        raise cherrypy.HTTPError(401, 'No token provided')

    token = auth_header[7:]
    try:
        key = get_key(token)
        payload = jwt.decode(token, key, algorithms=ALGORITHMS, issuer=ISSUER)
        cherrypy.request.user = payload
    except ExpiredSignatureError:
        raise cherrypy.HTTPError(401, 'Token has expired')
    except JWTError as e:
        raise cherrypy.HTTPError(401, f'Invalid token: {str(e)}')

cherrypy.tools.jwt_auth = cherrypy.Tool('before_handler', require_jwt)

class ProtectedAPI:
    @cherrypy.expose
    @cherrypy.tools.json_out()
    @cherrypy.tools.jwt_auth()
    def protected(self):
        return {'message': 'Access granted', 'user': cherrypy.request.user}

if __name__ == '__main__':
    cherrypy.quickstart(ProtectedAPI())
```

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