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

# Add JWT Authentication to Your Masonite API

Secure your Masonite API using JWT authentication with JWKS.

## How Zuplo Handles It

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

## Masonite Backend Code

```python
from masonite.request import Request
from masonite.response import Response
from masonite.middleware import Middleware
from masonite.routes import Route
from jose import jwt
from jose.exceptions import JWTError, ExpiredSignatureError
import requests
from cachetools import TTLCache

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

class JWTAuthMiddleware(Middleware):
    def before(self, request: Request, response: Response):
        auth_header = request.header('Authorization')
        if not auth_header or not auth_header.startswith('Bearer '):
            return response.json({'error': 'No token provided'}, status=401)

        token = auth_header[7:]
        try:
            key = get_key(token)
            payload = jwt.decode(token, key, algorithms=ALGORITHMS, issuer=ISSUER)
            request.user = payload
        except ExpiredSignatureError:
            return response.json({'error': 'Token has expired'}, status=401)
        except JWTError as e:
            return response.json({'error': 'Invalid token', 'details': str(e)}, status=401)

    def after(self, request: Request, response: Response):
        pass

# Controller
class ProtectedController:
    def show(self, request: Request, response: Response):
        return response.json({'message': 'Access granted', 'user': request.user})

# Routes (add to routes/web.py)
ROUTES = [
    Route.get('/protected', ProtectedController.show).middleware('jwt')
]

# Register middleware in Kernel.py:
# route_middleware = {
#     'jwt': JWTAuthMiddleware
# }
```

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