lambdaTriggers on a CognitoStack

I’m trying to setup custom auth triggers on cognito, but can’t seem to work out how to connect the lambda functions defined in my serverless.yml with my cognito stack being built with SST.

From what I can tell, I need to define the triggers when the stack is created, but I can’t quite work out how to pass the triggers in through props - how do I refer to the serverless functions in the creation of the stack?

My SST index:

import CognitoStack from "./CognitoStack";

export default function main(app) {
  new CognitoStack(app, "cognito", { triggers: "WHAT GOES HERE?" });
}

My cognito stack:

export default class CognitoStack extends sst.Stack {
  constructor(scope, id, props) {
    super(scope, id, props);

    const userPool = new cognito.UserPool(this, "UserPool", {
        signInAliases: { username: true, email: true, phone: true },
        lambdaTriggers: {
          defineAuthChallenge: props.triggers.defineAuthChallenge,
          createAuthChallenge: props.triggers.createAuthChallenge,
          verifyAuthChallengeResponse: props.triggers.verifyAuthChallengeResponse,
       },
       selfSignUpEnabled: false,
    });

    const userPoolClient = new cognito.UserPoolClient(this, "UserPoolClient", {
      userPool,
      authFlows: {
        custom: true,
        refreshToken: true,
      },
    });

    // Export values
    new CfnOutput(this, "UserPoolId", {
      value: userPool.userPoolId,
   });
   new CfnOutput(this, "UserPoolClientId", {
      value: userPoolClient.userPoolClientId,
  });
  }
}

And my serverless.yml:

custom:
  stage: ${opt:stage, self:provider.stage}
  sstApp: ${self:custom.stage}-auth-infra

provider:
  name: aws
  runtime: nodejs12.x
  stage: dev
  region: eu-west-1

functions:
  cognitoDefineAuthChallenge:
    handler: handler.defineAuthChallenge
    events:
      - cognitoUserPool:
          pool: !ImportValue ${self:custom.sstApp}-UserPoolId
          trigger: defineAuthChallenge
  cognitoCreateAuthChallenge:
    handler: handler.createAuthChallenge
    events:
      - cognitoUserPool:
          pool: !ImportValue ${self:custom.sstApp}-UserPoolId
          trigger: createAuthChallenge
  cognitoVerifyAuthChallengeResponse:
    handler: handler.verifyAuthChallengeResponse
    events:
      - cognitoUserPool:
          pool: !ImportValue ${self:custom.sstApp}-UserPoolId
          trigger: verifyAuthChallengeResponse

So under this setup, you are first deploying the SST app right? And then deploying your Lambda functions?

If that’s the case then you can’t really pass in the props because they wouldn’t have been created yet? I haven’t tried this exact scenario but in the guide we attach the Cognito Auth Role in Serverless Framework after SST deploys the Identity Pool.

Ah thanks, I’ve just come back to working on this again, and realised that I don’t have to define the Lambda triggers in the SST config, and just need to make sure that I export the UserPoolId properly in the Cognito stack with:

new CfnOutput(this, "UserPoolId", {
  value: userPool.userPoolId,
  exportName: app.logicalPrefixedName("UserPoolId"),
});

And refer to it in the serverless config in the same way you do in the tutorial:

defineAuthChallenge:
  handler: handlers/defineAuthChallenge.main
  events:
    - cognitoUserPool:
        pool: 
          Fn::ImportValue: ${self:custom.sstApp}-UserPoolId
        trigger: DefineAuthChallenge
        existing: true

Now I’m just dealing with a problem with the deploy where it fails in someway to create the custom auth handler with the line:

CloudFormation - CREATE_FAILED - Custom::CognitoUserPool - DefineAuthChallengeCustomCognitoUserPool1

First step is to work out how to see the log for this in Cloudwatch - I can’t see how to create a log group yet - I’m getting the logstream id from the deploy errors in Cloudformation, but when I look in Cloudwatch there are no log groups.

Ok, I didn’t work out what was going on with the logs, but I did work out how to get it to deploy.

Thanks to this post: https://forum.serverless.com/t/existing-cognito-pool-as-function-trigger/12491 I saw that I was using the User Pool Id, when serverless requires the name (and there’s no way to make it read from the id).

I’ve made it so that my cognito stack now gets named with a specific name I create from the stage and my own naming convention:

const userPool = new cognito.UserPool(this, "UserPool", {
  signInAliases: { username: true, email: true, phone: true },
  selfSignUpEnabled: false,
  userPoolName,
});

Note: this only works when you create the pool from scratch - CF won’t let you rename a pool - if you can’t blow away your pool, you’ll have to hard code the name in your serverless.yml - probably a bit of a mess if you have multiple environments.

And in my serverless.yml I set that as a custom variable that I configure the same way, and then use that in my function defs:

defineAuthChallenge:
handler: handlers/defineAuthChallenge.main
events:
  - cognitoUserPool:
      pool: ${self:custom.userPoolName}
      trigger: DefineAuthChallenge
      existing: true

It all now deploys and sets the triggers on the pool created by SST/CDK.

1 Like

Awesome! Thanks for sharing.