---
title: "Add JWT Authentication to Your undefined API"
description: undefined
canonicalUrl: "https://zuplo.com/use-cases/api-key-auth/javascript/koa/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

```javascript
const Koa = require("koa");
const Router = require("@koa/router");
const jwt = require("jsonwebtoken");
const jwksClient = require("jwks-rsa");

const app = new Koa();
const router = new Router();

// Replace with your actual Zuplo deployment name or custom domain
const ISSUER = "https://my-api-a32f34.zuplo.api/__zuplo/issuer";

// Create a JWKS client to fetch public keys
const client = jwksClient({
  jwksUri: `${ISSUER}/.well-known/jwks.json`,
  cache: true,
  cacheMaxAge: 600000, // 10 minutes
});

// Function to get the signing key
async function getKey(header) {
  return new Promise((resolve, reject) => {
    client.getSigningKey(header.kid, (err, key) => {
      if (err) {
        return reject(err);
      }
      const signingKey = key.getPublicKey();
      resolve(signingKey);
    });
  });
}

// Middleware to validate JWT
async function validateJwt(ctx, next) {
  const authHeader = ctx.headers.authorization;
  const token = authHeader && authHeader.replace("Bearer ", "");

  if (!token) {
    ctx.status = 401;
    ctx.body = { error: "No token provided" };
    return;
  }

  try {
    const key = await getKey(jwt.decode(token, { complete: true }).header);
    const decoded = jwt.verify(token, key, {
      issuer: ISSUER,
      algorithms: ["RS256"],
    });
    ctx.state.user = decoded;
    await next();
  } catch (err) {
    ctx.status = 401;
    ctx.body = { error: "Invalid token", details: err.message };
  }
}

// Example usage
router.get("/protected", validateJwt, (ctx) => {
  ctx.body = {
    message: "Access granted",
    user: ctx.state.user,
  };
});

app.use(router.routes()).use(router.allowedMethods());

app.listen(3000, () => {
  console.log("Server running on http://localhost:3000");
});
```

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