View on GitHub

lambda-middleware

A collection of middleware for AWS lambda functions.

@lambda-middleware/jwt-auth

npm version downloads open issues debug build status codecov dependency status devDependency status

A middleware for AWS http lambda functions to verify JWT auth tokens inspired by express-jwt.

Lambda middleware

This middleware is part of the lambda middleware series. It can be used independently.

Usage

import { compose } from "@lambda-middleware/compose";
import { errorHandler } from "@lambda-middleware/http-error-handler";
import { jwtAuth, EncryptionAlgorithms, AuthorizedEvent } from "@lambda-middleware/jwt-auth";
import { APIGatewayEvent } from "aws-lambda";
import createHttpError from "http-errors";

interface TokenPayload {
  permissions: string[];
}

function isTokenPayload(token: any): token is TokenPayload {
  return (
    token != null &&
    Array.isArray(token.permissions) &&
    token.permissions.every((permission: any) => typeof permission === "string")
  );
}

// This is your AWS handler
const helloWorld = async (
  event: APIGatewayEvent & AuthorizedEvent<TokenPayload>
) => {
  // The middleware adds auth information if a valid token was added
  // If no auth was found and credentialsRequired is set to true, a 401 will be thrown. If auth exists you
  // have to check that it has the expected form.
  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}`,
    }),
    statusCode: 200,
  };
};

export const handler = compose(
  errorHandler(),
  jwtAuth({
    /** Algorithm to verify JSON web token signature */
    algorithm: EncryptionAlgorithms.HS256,
    /** An optional boolean that enables making authorization mandatory */
    credentialsRequired: true,
    /**
     * An optional function that checks whether the token payload is formatted correctly.
     * If you specify a TokenPayload type parameter to AuthorizedEvent, the corresponding type guard must be passed here.
     */
    isPayload: isTokenPayload,
    /** A string or buffer containing either the secret for HMAC algorithms, or the PEM encoded public key for RSA and ECDSA */
    secretOrPublicKey: "secret",
    /**
     * An optional function used to search for a token e. g. in a query string. By default, and as a fall back,
     * event.headers.authorization and event.headers.Authorization are used.
     */
    tokenSource: (event: any) => event.queryStringParameters.token,
  })
)(helloWorld);

Please note that there is some TypeScript magic going on that checks if credentialsRequired is set to true, and if it isn’t, will check that your handler does not require on event.auth being set. Unfortunately this means that if you define options as a variable before handing them into the middleware, you have to define them const options = { ... } as const so TypeScript reads credentialsRequired as true instead of expanding the type to boolean.

If you directly hand in the options to the middleware, this should not be snecessary.