Hey. I am trying to set up Cognito Userpool. Ever...
# help
s
Hey. I am trying to set up Cognito Userpool. Everything works fine, just got one question. I want to allow this
Copy code
Username - Users can use a username and optionally multiple alternatives to sign up and sign in.
I want to allow Users to sign in/out with both Username & Email. I want both these attributes to be required. How do I achieve this? BR, Saman
My code:
Copy code
Resources:
  CognitoUserPool:
    Type: AWS::Cognito::UserPool
    Properties:
      # Generate a name based on the stage
      UserPoolName: ${self:provider.stage}-user-pool
      # Set email as an alias
      Policies: 
          PasswordPolicy:
            MinimumLength: 8
            RequireLowercase: false
            RequireNumbers: false
            RequireSymbols: false
            RequireUppercase: false
      Schema:
          -
            AttributeDataType: String
            Mutable: true
            Name: "image_url"
          -
            AttributeDataType: String
            Mutable: true
            Name: "username"
      UsernameAttributes:
        - email
      AutoVerifiedAttributes:
        - email
      EmailConfiguration:
        EmailSendingAccount: COGNITO_DEFAULT
f
Hey @Saman -, it’s been a while since I last configured a user pool. If i remembered correctly, you want to use
preferred_username
instead of
username
for actually Username. B/c
username
cannot be changed.
So the allow users sign in with both
preferred_username
and
email
, you’d want to set:
Copy code
AliasAttributes:
  - email
  - preferred_username
s
Wow @Frank you are a hero
Hmm... I am getting this
Copy code
An error occurred: CognitoUserPool - Updates are not allowed for property - AliasAttributes. (Service: AWSCognitoIdentityProvider; Status Code: 400; Error Code: InvalidParameterException; Request ID: null; Proxy: null).
f
Yeah AliasAttributes cannot be updated once created. You’d need to recreate the user pool.
s
Getting following error now after deleting the entire stack and trying to recreate.
Copy code
No export named develop-UserPoolId found.
My yml
Copy code
# Print out the Id of the User Pool that is created
Outputs:
  UserPoolId:
    Value:
      Ref: CognitoUserPool
    Export:
      Name: TrainoApp-${self:provider.stage}-UserPoolId

  UserPoolClientId:
    Value:
      Ref: CognitoUserPoolClient
    Export:
        Name: TrainoApp-${self:provider.stage}-UserPoolClientId
f
@Saman - Can you show me where you are trying import
${self:provider.stage}-UserPoolId
in your yml?
s
So this is my cognito resource
Copy code
Resources:
  CognitoUserPool:
    Type: AWS::Cognito::UserPool
    Properties:
      # Generate a name based on the stage
      UserPoolName: ${self:provider.stage}-user-pool
      # Set email as an alias
      Policies: 
          PasswordPolicy:
            MinimumLength: 8
            RequireLowercase: false
            RequireNumbers: false
            RequireSymbols: false
            RequireUppercase: false
      Schema:
          -
            AttributeDataType: String
            Mutable: true
            Name: "image_url"

      UsernameAttributes:
        - email
        - preferred_username
      AutoVerifiedAttributes:
        - email
        - preferred_username
      EmailConfiguration:
        EmailSendingAccount: COGNITO_DEFAULT


  CognitoUserPoolClient:
    Type: AWS::Cognito::UserPoolClient
    Properties:
      # Generate an app client name based on the stage
      ClientName: ${self:provider.stage}-user-pool-client
      UserPoolId:
        Ref: CognitoUserPool
      ExplicitAuthFlows:
        - ADMIN_NO_SRP_AUTH
      GenerateSecret: false

# Print out the Id of the User Pool that is created
Outputs:
  UserPoolId:
    Value:
      Ref: CognitoUserPool
    Export:
      Name: ${self:provider.stage}-UserPoolId

  UserPoolClientId:
    Value:
      Ref: CognitoUserPoolClient
    Export:
        Name: ${self:provider.stage}-UserPoolClientId
and this is the lambda resource that uses the export name
Copy code
isEmailBusy:
  handler: handler/user/user.isEmailBusy
  environment:
     USER_POOL_ID: !ImportValue ${self:provider.stage}-UserPoolId
     USER_POOL_CLIENT_ID: !ImportValue ${self:provider.stage}-UserPoolClientId
     USER_POOL_PW: ${env:USER_POOL_PW}
     USER_POOL_USER: ${env:USER_POOL_USER}
     ACCESS_KEY_ID: ${env:ACCESS_KEY_ID}
     SECRET_ACCESS_KEY: ${env:SECRET_ACCESS_KEY}
  events: 
    - http:
        method: POST
        path: /isemailavailable
  
isUsernameBusy:
  handler: handler/user/user.isUsernameBusy
  environment:
     USER_POOL_ID: !ImportValue ${self:provider.stage}-UserPoolId
     USER_POOL_CLIENT_ID: !ImportValue ${self:provider.stage}-UserPoolClientId
     USER_POOL_PW: ${env:USER_POOL_PW}
     USER_POOL_USER: ${env:USER_POOL_USER}
     ACCESS_KEY_ID: ${env:ACCESS_KEY_ID}
     SECRET_ACCESS_KEY: ${env:SECRET_ACCESS_KEY}
  events: 
    - http:
        method: POST
        path: /isusernameavailable
@Frank
f
It seems your Lambda function is trying to import
!ImportValue ${self:provider.stage}-UserPoolId
the UserPoolId but it hasn’t been created.
Are the both snippets above from the same
serverless.yml
?
s
Yes, both are part of serverless.yml
Could it be that it does not finish creating the cognitouserpool before trying to create the lambda?
@Frank
Update: Had to deploy the cognito first for some reason. Works now 🙂
f
Yeah, you should use
Ref
instead of
ImportValue
b/c the former sets a inter-resource dependency so the User Pool will gets created first.
Only use the latter to share data across different `serverless.yml`s
Whenever u got some free time, give SST a spin. You are forced to deal with a lot of these issues in
serverless.yml
b/c you are writing raw CloudFormation. They are all taken care of out of the box with SST. 🤓
s
Alright @Frank thanks for the tip!