Question: Your Cognito JWT examples use the CDK `C...
# help
m
Question: Your Cognito JWT examples use the CDK
Cognito
construct. Can the same be achieved with the
sst.Auth
construct? I was wondering why my User Pool wasn't showing up in the console when I realized this.
f
Hey @Michael Clifford, for sure. Instead of
Copy code
// Create User Pool
    const userPool = new cognito.UserPool(this, "UserPool", {
      selfSignUpEnabled: true,
      signInAliases: { email: true },
      signInCaseSensitive: false,
    });

    // Create User Pool Client
    const userPoolClient = new cognito.UserPoolClient(this, "UserPoolClient", {
      userPool,
      authFlows: { userPassword: true },
    });
Do this
Copy code
const auth = new sst.Auth(this, "Auth", {
      cognito: {
        userPool: {
          selfSignUpEnabled: true,
          signInAliases: { email: true },
          signInCaseSensitive: false,
        },
        userPoolClient: {
          authFlows: { userPassword: true },
        }
      }
    });

    // auth.cognitoUserPool
    // auth.cognitoUserPoolClient
We should update the example with this.
m
@Frank I finally got around to making these changes but am seeing an error I wasn't seeing before, related to passing in a function created in another stack to be used as a trigger:
Copy code
Error: 'cliffom-sst-bff-demo-user-tasks-stack' depends on 'cliffom-sst-bff-demo-auth-stack' (cliffom-sst-bff-demo-user-tasks-stack -> cliffom-sst-bff-demo-auth-stack/Auth/UserPool/Resource.Arn). Adding this dependency (cliffom-sst-bff-demo-auth-stack -> cliffom-sst-bff-demo-user-tasks-stack/postConfirmationHandler/Resource.Arn) would create a cyclic reference.
The new code looks like this:
Copy code
// Create User Pool and User Pool Client
    const auth = new sst.Auth(this, 'Auth', {
      cognito: {
        userPool: {
          selfSignUpEnabled: true,
          signInAliases: {email: true},
          signInCaseSensitive: false,
        },
        userPoolClient: {
          authFlows: {userPassword: true},
          idTokenValidity: Duration.days(1),
        },
        triggers: {
          postConfirmation: props?.postConfirmationFunction,
        },
      },
    });
It was working before but with the raw cognito CDK construct
Copy code
// Create User Pool
    const userPool = new cognito.UserPool(this, 'UserPool', {
      selfSignUpEnabled: true,
      signInAliases: {email: true},
      signInCaseSensitive: false,
      lambdaTriggers: {
        postConfirmation: props?.postConfirmationFunction,
      },
    });

    // Create User Pool Client
    const userPoolClient = new cognito.UserPoolClient(this, 'UserPoolClient', {
      userPool,
      authFlows: {userPassword: true},
      idTokenValidity: Duration.days(1),
    });
I'm doing it this way because I want to avoid putting a direct dependency on/awareness of a DynamoDB table into the Auth stack. Outside the deps look like this:
Copy code
// Create our single DynamoDB table
  const tableStack = new TableStack(app, 'table-stack');

  // Create our user tasks stack
  const userTasksStack = new UserTasksStack(app, 'user-tasks-stack', {
    table: tableStack.table,
  });

  // Create our Auth stack that defines our Cognito pool and client
  const authStack = new AuthStack(app, 'auth-stack', {
    postConfirmationFunction: userTasksStack.createUserFunction,
  });
f
Are you doing an
auth.attachPermissionsForAuthUsers
call anywhere?
m
@Frank I am not
f
Alright, did some digging. So if u were to use the raw CDK construct, this WORKS:
Copy code
const userPool = new cognito.UserPool(this, 'UserPool', {
  ...
  lambdaTriggers: {
    postConfirmation: props?.postConfirmationFunction,
  },
});
But this DOES NOT WORK, you will get the same cyclical dependency error.
Copy code
const userPool = new cognito.UserPool(this, 'UserPool', {
  ...
});
userPool.addTrigger(cognito.UserPoolOperation.POST_CONFIRMATION, props?.postConfirmationFunction);
The reason being when you configure a trigger, an
AWS::Lambda::Permission
resource is created in the UserTasks stack saying that ONLY this cognito pool can trigger this Lambda function (for security purposes). This causes the cyclical dependency error b/c •
UserTasksStack
needs to know `AuthStack`’s UserPool ARN; and •
AuthStack
needs to know `UserTasksStack`’s function ARN
However, when you pass the triggers inline as in the former case, the User Pool hasn’t been created yet. So CDK by mistake creates a
AWS::Lambda::Permission
with
undefined
ARN, and any cognito user pool can trigger this function.
Also due to this bug,
UserTasksStack
 doesn’t depend on 
AuthStack
Just to sum it up,
sst.Auth
always calls the
addTrigger
function, causing the cyclical dependency error.
Hope i’m explaining this clearly lol.
Would it work if you defined the PostConfirmation function in the same stack as
Auth
?
m
@Frank I will try that and I'm confident it will work, but I was hoping to avoid that
f
I see.. we could cut a release to intentionally introduce this “bug”, but I’m almost sure it will get brought up to CDK and they will fix this. And by then, it will break again.
The bottom line is, the Lambda function needs to have an
AWS::Lambda::Permission
resource referencing the User Pool, and the
AWS::Cognito::UserPool
needs to reference the function’s ARN.
You can’t create them in two different stacks.
It’s a design flaw of CloudFormation. And they have some plans to fix this by introducing a new resource ie.
AWS::Cognito::LambdaTrigger
to break this cyclical dependency.
m
got it, thanks @Frank!
I'm going to move the function into the auth stack and just pass in the DynamoDB table as a dep