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

# Add JWT Authentication to Your ActionHero API

Secure your ActionHero API using JWT authentication with JWKS.

## How Zuplo Handles It

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

## ActionHero Backend Code

```typescript
import { Action, api } from "actionhero";
import jwt from "jsonwebtoken";
import jwksClient from "jwks-rsa";

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);
    callback(null, key?.getPublicKey());
  });
}

async function validateJwt(token: string): Promise<any> {
  return new Promise((resolve, reject) => {
    jwt.verify(
      token,
      getKey,
      { issuer: ISSUER, algorithms: ["RS256"] },
      (err, decoded) => {
        if (err) reject(err);
        else resolve(decoded);
      },
    );
  });
}

export class ProtectedAction extends Action {
  constructor() {
    super();
    this.name = "protected";
    this.description = "A protected endpoint requiring JWT authentication";
    this.outputExample = { message: "Access granted" };
  }

  async run(data: any) {
    const authorization =
      data.connection.rawConnection.req.headers.authorization;

    if (!authorization || !authorization.startsWith("Bearer ")) {
      data.connection.rawConnection.responseHttpCode = 401;
      return { error: "No token provided" };
    }

    const token = authorization.slice(7);

    try {
      const decoded = await validateJwt(token);
      return { message: "Access granted", user: decoded };
    } catch (err: any) {
      data.connection.rawConnection.responseHttpCode = 401;
      return { error: "Invalid token", details: err.message };
    }
  }
}
```

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