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

# Add JWT Authentication to Your Feathers API

Secure your Feathers API using JWT authentication with JWKS.

## How Zuplo Handles It

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

## Feathers Backend Code

```typescript
import { feathers } from "@feathersjs/feathers";
import express, {
  rest,
  json,
  urlencoded,
  errorHandler,
} from "@feathersjs/express";
import jwt from "jsonwebtoken";
import jwksClient from "jwks-rsa";
import { Request, Response, NextFunction } from "express";

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

const client = jwksClient({
  jwksUri: `${ISSUER}/.well-known/jwks.json`,
  cache: true,
  cacheMaxAge: 600000,
});

function getKey(
  header: jwt.JwtHeader,
  callback: (err: Error | null, key?: string) => void,
) {
  client.getSigningKey(header.kid!, (err, key) => {
    if (err) return callback(err);
    const signingKey = key?.getPublicKey();
    callback(null, signingKey);
  });
}

const jwtMiddleware = async (
  req: Request,
  res: Response,
  next: NextFunction,
) => {
  const token = req.headers.authorization?.replace("Bearer ", "");

  if (!token) {
    return res.status(401).json({ error: "No token provided" });
  }

  jwt.verify(
    token,
    getKey,
    { issuer: ISSUER, algorithms: ["RS256"] },
    (err, decoded) => {
      if (err) {
        return res
          .status(401)
          .json({ error: "Invalid token", details: err.message });
      }
      (req as any).user = decoded;
      next();
    },
  );
};

const app = express(feathers());

app.use(json());
app.use(urlencoded({ extended: true }));
app.configure(rest());

app.use("/protected", jwtMiddleware);
app.use("/protected", {
  async find(params: any) {
    return { message: "Access granted", user: params.user };
  },
});

app.use(errorHandler());

app.listen(3030).then(() => {
  console.log("Feathers server listening on port 3030");
});
```

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