Middy JWT Authorization Middleware

raw JSON →
6.3.0 verified Thu Apr 23 auth: no javascript

middy-middleware-jwt-auth is a specialized middleware designed for the Middy.js framework, enabling JSON Web Token (JWT) authorization for AWS Lambda functions. Inspired by `express-jwt`, it simplifies the process of verifying JWTs and injecting the decoded payload into the Lambda `event.auth` object. The current stable version is `6.3.0`. The library follows the Middy.js release cadence, frequently updating to support new major versions of `@middy/core`. Key differentiators include its tight integration with the Middy ecosystem, robust type definitions for TypeScript users, and customizable token extraction and verification options, including support for various encryption algorithms and asynchronous secret retrieval. It aims to provide a reliable and easy-to-use solution for securing serverless API endpoints with JWTs.

error TypeError: (0, middy_middleware_jwt_auth_1.JWTAuthMiddleware) is not a function
cause Attempting to import `JWTAuthMiddleware` using a named import syntax when it is a default export.
fix
Change the import statement to import JWTAuthMiddleware from 'middy-middleware-jwt-auth';
error Error: 'jwt malformed' or 'invalid signature'
cause The provided JWT is not a valid JSON Web Token structure, or its signature does not match the provided `secretOrPublicKey`.
fix
Verify that the token sent in the Authorization header (typically Bearer <token>) is correctly formatted and that the secretOrPublicKey configured in the middleware matches the key used to sign the token.
error Error: 'No authorization token was found'
cause The `Authorization` header is missing or empty, and `credentialsRequired` is set to `true` in the middleware options.
fix
Ensure the client sends a valid Authorization: Bearer <token> header, or if authentication is optional, set credentialsRequired: false in the middleware configuration.
error TypeError: event.auth is undefined
cause Attempting to access `event.auth` without checking if a token was present/valid, or when `credentialsRequired` is `false` and no token was provided.
fix
Always check for the existence of event.auth before accessing its properties: if (event.auth && event.auth.payload) { ... }. Alternatively, ensure credentialsRequired: true if the auth object is strictly needed.
breaking Version 6.0.0 introduced a breaking change by upgrading its underlying `jsonwebtoken` dependency to version 9. Users must review the migration notes for `jsonwebtoken` v9, as related breaking changes (e.g., in options for `sign` or `verify` methods) now apply to `middy-middleware-jwt-auth`.
fix Consult `https://github.com/auth0/node-jsonwebtoken/wiki/Migration-Notes` for `jsonwebtoken` v9. Update your `secretOrPublicKey` and other JWT options accordingly.
gotcha The `JWTAuthMiddleware` is a default export. Attempting to import it as a named export (e.g., `import { JWTAuthMiddleware } from '...'`) will lead to runtime errors like `TypeError: (0, middy_middleware_jwt_auth_1.JWTAuthMiddleware) is not a function` in CommonJS contexts or `undefined` in ESM.
fix Always import `JWTAuthMiddleware` as a default export: `import JWTAuthMiddleware from 'middy-middleware-jwt-auth';`
gotcha The middleware's `credentialsRequired` option defaults to `false`. This means if no `Authorization` header is present or the token is invalid, the middleware will proceed without throwing an error, but `event.auth` will be `undefined`. You must explicitly set it to `true` to enforce authentication for a given handler.
fix To make authentication mandatory, set `credentialsRequired: true` in the middleware options: `JWTAuthMiddleware({ ..., credentialsRequired: true })`.
gotcha Since version 6.3.0, `secretOrPublicKey` and `tokenSource` can be asynchronous functions. While this offers flexibility (e.g., fetching secrets from Secrets Manager), ensure your implementation correctly handles promises and callbacks, as incorrect usage might lead to verification failures or unexpected delays.
fix When using `secretOrPublicKey` or `tokenSource` as functions, make sure they correctly return a Promise or use the provided callback. Example for `secretOrPublicKey` with a callback: `secretOrPublicKey: (header, payload, done) => { someAsyncFetch().then(secret => done(null, secret)).catch(done); }`.
npm install middy-middleware-jwt-auth
yarn add middy-middleware-jwt-auth
pnpm add middy-middleware-jwt-auth

This quickstart demonstrates how to set up a Middy handler with `middy-middleware-jwt-auth` to enforce JWT authentication, extract the token payload, and perform fine-grained authorization checks based on the decoded token's content.

import createHttpError from "http-errors";
import middy from "@middy/core";
import httpErrorHandler from "@middy/http-error-handler";
import httpHeaderNormalizer from "@middy/http-header-normalizer";
import JWTAuthMiddleware, {
  EncryptionAlgorithms,
  IAuthorizedEvent,
} from "middy-middleware-jwt-auth";

// Define the token payload structure expected from your JWT
interface ITokenPayload {
  permissions: string[];
}

// Type guard for the token payload to ensure runtime safety
function isTokenPayload(token: any): token is ITokenPayload {
  return (
    token != null &&
    Array.isArray(token.permissions) &&
    token.permissions.every((permission: any) => typeof permission === "string")
  );
}

// Your AWS Lambda handler function
const helloWorld = async (event: IAuthorizedEvent<ITokenPayload>) => {
  // Access the authenticated payload from event.auth
  if (!event.auth || !isTokenPayload(event.auth.payload)) {
    throw createHttpError(401, "Unauthorized: Invalid token payload");
  }

  // Perform authorization check based on permissions in the token
  if (event.auth.payload.permissions.indexOf("helloWorld") === -1) {
    throw createHttpError(
      403,
      `User not authorized for helloWorld, only found permissions [${event.auth.payload.permissions.join(", ")}]`,
      {
        type: "NotAuthorized",
      },
    );
  }

  return {
    body: JSON.stringify({
      data: `Hello world! Here's your token: ${event.auth.token}`,
      userId: event.auth.payload.sub // Assuming 'sub' is in your token
    }),
    statusCode: 200,
  };
};

// 'Middyfy' your handler and attach the JWT authorization middleware
export const handler = middy(helloWorld)
  .use(httpHeaderNormalizer()) // Ensures Authorization header is consistently cased
  .use(httpErrorHandler())    // Catches errors thrown by JWTAuthMiddleware and returns appropriate HTTP responses
  .use(
    JWTAuthMiddleware({
      algorithm: EncryptionAlgorithms.HS256,
      credentialsRequired: true, // Set to true to make a missing or invalid token result in a 401
      secretOrPublicKey: process.env.JWT_SECRET ?? 'supersecretkey',
      // You can also specify an async function for secretOrPublicKey or tokenSource since v6.3.0
      // secretOrPublicKey: async (header, payload, done) => { /* fetch secret */ done(null, 'secret'); },
      // tokenSource: (event) => event.headers['x-custom-token'],
    }),
  );