How to add JWT authorization with Cognito User Pool to a serverless API

Link to chapter — https://serverless-stack.com/examples/how-to-add-jwt-authorization-with-cognito-user-pool-to-a-serverless-api.html

Was able to get the example working with no issues, all good on that side.

From the perspective of using this in some kind of production scenario though, the code creates a new userpool and userpoolclient every time since it’s wrapped in a stack.

After some playing around, I was able to get JWT authorization working using an AuthStack (based on the example in the Guide) so that the cognito entities are maintained between invocations, however there are a couple of caveats:

AuthStack.js:

import * as cognito from "aws-cdk-lib/aws-cognito";
import * as sst from "@serverless-stack/resources";

export default class AuthStack extends sst.Stack {

  // Public reference to the api
  auth;

  constructor(scope, id, props) {
    super(scope, id, props);
   
    this.auth = new sst.Auth(this, 'auth', {
      cognito: {
        userPool: {
          signInAliases: { email: true },
        }
      }
    })

    // Show the API endpoint and other info in the output
    this.addOutputs({
      Region: scope.region,
      JWTUserPoolId: this.auth.cognitoUserPool.userPoolId,
      JWTUserPoolClientId: this.auth.cognitoUserPoolClient.userPoolClientId,
    });

  }
}

ApiStack.js:

import * as cognito from "aws-cdk-lib/aws-cognito";
import * as apigAuthorizers from "@aws-cdk/aws-apigatewayv2-authorizers-alpha";
import * as sst from "@serverless-stack/resources";

export default class ApiStack extends sst.Stack {

  // Public reference to the api
  api;

  constructor(scope, id, props) {
    super(scope, id, props);
   
    const { auth } = props;

    this.api = new sst.Api(this, id, {
      defaultAuthorizer: new apigAuthorizers.HttpUserPoolAuthorizer(
        "Authorizer",
        auth.cognitoUserPool,
        {
          userPoolClients: [auth.cognitoUserPoolClient],
        }
      ),
      defaultAuthorizationType: sst.ApiAuthorizationType.JWT,
      routes: {
        "GET /private": "src/private.handler",
        "GET /public": {
          function: "src/public.handler",
          authorizationType: sst.ApiAuthorizationType.NONE,
        },
      },
    });

    // Show the API endpoint and other info in the output
    this.addOutputs({
      ApiEndpoint: this.api.url,
    });

  }
}

index.js:

import ApiStack from "./ApiStack";
import AuthJWTStack from "./AuthJWTStack";

export default function main(app) {
  // Set default runtime for all functions
  app.setDefaultFunctionProps({
    runtime: "nodejs14.x"
  });

  const authStack = new AuthJWTStack(app, "auth");

  const apiStack = new ApiStack(app, "api", {
    auth: authStack.auth
  });

}

Caveats

  1. I had to manually enable the “USER_PASSWORD_AUTH” on the auto-created App Client through the AWS console. Is there any way to require this as part of the AuthStack cognito configuration?

  1. Not an issue so much, as excess - in this scenario there is no(?) need for an Identity Pool. That being the case is there any way to prevent the Auth stack from auto-creating one?

Cheers,

Shaun

Yeah you’ll be able to turn off the identity pool in v1: Auth | Serverless Stack (SST)

I’m not totally sure about the USER_PASSWORD_AUTH, you might want to ask in Slack!