Cognito User/Identity Pools as serverless.yml resource defs


#1

I’m having trouble finding an example so forgive me if it’s my search skills that are lacking here, but has anybody sucessfully set up the Resources section in your serverless.yml file to construct User and Identity Pools automatically?

Really, I’m trying to automate what @fanjie and @jayair have done here:



Custom authorizer with cognito user pool service
#2

In your serverless yaml file, you can use cloud formation template to create cognito pool as shown below and then you can use the output of the cognito pool how ever you want, in this example, I created an env variable that will be injected to lambda’s.

Hope this helps.

 environment:
USER_POOL:
   'Fn::GetAtt': [ userPool, Arn ]


userPool:
    Type: AWS::Cognito::UserPool
    Properties:
      AdminCreateUserConfig:
        AllowAdminCreateUserOnly: true
      #AliasAttributes:
      #  - String
      #AutoVerifiedAttributes:
      #  - String
      #DeviceConfiguration:
      #  DeviceConfiguration
      #EmailConfiguration:
      #  EmailConfiguration
      #EmailVerificationMessage: String
      #EmailVerificationSubject: String
      #LambdaConfig:
      #  LambdaConfig
      #MfaConfiguration: String
      #Policies:
      #  Policies
      UserPoolName: ${self:provider.custom.stage}-XXXX
      Schema:
        - AttributeDataType: String
          Mutable: true
          Name: role
      #SmsAuthenticationMessage: String
      #SmsConfiguration:
      #  SmsConfiguration
      #SmsVerificationMessage: String
      #UserPoolTags:
      #  String: String

#3

I’m also having trouble with this, I can create the user pool, but I can’t seem to reference the user pool as a cognito provider in the identity pool section, and it fails saying the provider name is wrong.

It’s hard to find sample for these things.


#4

That syntax works great for the environment variables, but I’m trying to set the authorizer arn and this:

    events:
  - http:
      path: location
      method: post
      cors: true
      authorizer:
        arn: 'Fn::GetAtt': [ UserPool, Arn ]

Fails, I’m guessing in part because of order of operations. What I’d like is for the Resources to generate and then fetch the arn to put as my authorizer.


#5

try with ‘Fn::GetAtt’: [ UserPool, Arn ] on a new line


#6

You can still reference the environment variable within the authorizer or define the environment variable for that function. Haven’t tried it but try and see what happens.

If there is dependency issue, Cloud Front provides depends on clause you can use that so you can manage the order of resource creation


#7

@altcatalin Thanks for the idea, but it still barfs on it on a new line.

@pgali The environment variable can be read in the function no problem, but I’m trying to get the framework to set up the authorizer for me automatically. That may be asking to much, I’m beginning to think 8).


#8

have you also tried with the long function form?
http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference-getatt.html

this works

functions:
  ConsumerFunction:
    ...
    environment:
      QUEUE_ARN:
        Fn::GetAtt:
          - Queue
          - Arn

resources:  # CloudFormation template syntax
  Resources:

    # SQS
    Queue:
      Type: AWS::SQS::Queue
      ...

#9

FYI, just got confirmation that this is a bug that will be fixed as part of a larger ARN issue, workaround described here:

Basically, create your stack once, come back to your serverless.yml and hard code the ARN that gets generated.


Dynamic ARN to Cognito authorizer
#10

I found this gist on github which helped solve a lot of my problems: https://gist.github.com/singledigit/2c4d7232fa96d9e98a3de89cf6ebe7a5


#11

I’ve gotten this to work with text message verification using gist from @barticus… but how it works with email verification I am not sure.


#12

Has anyone found out more about this. I’m looking to do the exact same thing you @nerdguru.

Can’t see to get the authorizer to take in a GetAtt call to the resource. I keep getting the SLS error
functionArn.split is not a function


#13

I was able to get serverless to generate my identity pool using the following:

IdentityPool:
  Type: "AWS::Cognito::IdentityPool"
  Properties:
    IdentityPoolName: identitypoolname
    AllowUnauthenticatedIdentities: true
    CognitoIdentityProviders: 
      - ClientId:
          Ref: UserPoolClient
        ProviderName:
          'Fn::GetAtt': [ MyUserPool, ProviderName ]

Hope this helps


#14

This will setup userpools that are compatible with aws-amplify

service:
  name: digiStatic

frameworkVersion: ">=1.1.0 <2.0.0"

provider:
  name: aws
  runtime: nodejs6.10
  memorySize: 512
  # Serverless variables like ${self:custom.prefix} We use variableSyntax here
  # to change serverless vars to look instead like $<self:custom.prefix>
  variableSyntax: '\$<([ :a-zA-Z0-9._,\\-\\/\\(\\)]+?)>'

plugins:
  - serverless-stack-output

custom:
  output:
    handler: scripts/output.handler # Same syntax as you already know
    file: ../stack.json # toml, yaml, yml, and json format is available

resources:
  Resources:

    # Creates a user pool in cognito for your app to auth against

    # Fixme: customer email address needs to be manually entered and verified
    # https://console.aws.amazon.com/ses/home?region=us-east-1#verified-senders-email:

    # Time based MFA can not be enabled from script
    # I have asked AWS to fix this

    UserPool:
      Type: "AWS::Cognito::UserPool"
      Properties:
        UserPoolName: $<self:service>-user-pool
        SmsVerificationMessage: "Your verification code is {####}."
        AutoVerifiedAttributes:
          - email
        MfaConfiguration: "OFF"
        EmailVerificationSubject: "Your Digispeaker verification code"
        EmailVerificationMessage: "Your Digispeaker verification code is {####}."
        SmsAuthenticationMessage: "Your Digispeaker authentication code is {####}."
        Schema:
          - Name: name
            AttributeDataType: String
            Mutable: true
            Required: false
          - Name: email
            AttributeDataType: String
            Mutable: false
            Required: true
          - Name: phone_number
            AttributeDataType: String
            Mutable: true
            Required: false
        Policies:
          PasswordPolicy:
            RequireLowercase: true
            RequireSymbols: false
            RequireNumbers: true
            MinimumLength: 8
            RequireUppercase: true
        AdminCreateUserConfig:
          InviteMessageTemplate:
            EmailMessage: "Your Digispeaker username is {username} and temporary password is {####}."
            EmailSubject: "Your temporary Digispeaker password"
            SMSMessage: "Your Digispeaker username is {username} and temporary password is {####}."
          UnusedAccountValidityDays: 7
          AllowAdminCreateUserOnly: false

    # Creates a User Pool Client to be used by the identity pool
    UserPoolClient:
      Type: "AWS::Cognito::UserPoolClient"
      Properties:
        ClientName: $<self:service>-client
        GenerateSecret: false
        UserPoolId: 
          Ref: UserPool
    
    # Creates a federeated Identity pool
    IdentityPool:
      Type: "AWS::Cognito::IdentityPool"
      Properties:
        IdentityPoolName: $<self:service>Identity
        AllowUnauthenticatedIdentities: true
        CognitoIdentityProviders: 
          - ClientId: 
              Ref: UserPoolClient
            ProviderName:
              'Fn::GetAtt': [ UserPool, ProviderName ]
        SupportedLoginProviders:
          'graph.facebook.com': "xxxxxxxxxx"
        OpenIdConnectProviderARNs:
          - 'arn:aws:iam::xxxxxxxxxxx:oidc-provider/accounts.google.com'

    # Create a role for unauthorized acces to AWS resources. Very limited access. Only allows users in the previously created Identity Pool
    CognitoUnAuthorizedRole:
      Type: "AWS::IAM::Role"
      Properties:
        AssumeRolePolicyDocument: 
          Version: "2012-10-17"
          Statement:
            - Effect: "Allow"
              Principal: 
                Federated: "cognito-identity.amazonaws.com"
              Action: 
                - "sts:AssumeRoleWithWebIdentity"
              Condition:
                StringEquals: 
                  "cognito-identity.amazonaws.com:aud":
                    Ref: IdentityPool
                "ForAnyValue:StringLike":
                  "cognito-identity.amazonaws.com:amr": unauthenticated
        Policies:
          - PolicyName: "CognitoUnauthorizedPolicy"
            PolicyDocument: 
              Version: "2012-10-17"
              Statement: 
                - Effect: "Allow"
                  Action:
                    - "mobileanalytics:PutEvents"
                    - "cognito-sync:*"
                  Resource: "*"

    # Create a role for authorized acces to AWS resources. Control what your user can access. This example only allows Lambda invokation
    # Only allows users in the previously created Identity Pool
    CognitoAuthorizedRole:
      Type: "AWS::IAM::Role"
      Properties:
        AssumeRolePolicyDocument: 
          Version: "2012-10-17"
          Statement:
            - Effect: "Allow"
              Principal: 
                Federated: "cognito-identity.amazonaws.com"
              Action: 
                - "sts:AssumeRoleWithWebIdentity"
              Condition:
                StringEquals: 
                  "cognito-identity.amazonaws.com:aud":
                    Ref: IdentityPool
                "ForAnyValue:StringLike":
                  "cognito-identity.amazonaws.com:amr": authenticated
        Policies:
          - PolicyName: "CognitoAuthorizedPolicy"
            PolicyDocument: 
              Version: "2012-10-17"
              Statement: 
                - Effect: "Allow"
                  Action:
                    - "mobileanalytics:PutEvents"
                    - "cognito-sync:*"
                    - "cognito-identity:*"
                  Resource: "*"
                - Effect: "Allow"
                  Action:
                    - "lambda:InvokeFunction"
                  Resource: "*"
    
    # Assigns the roles to the Identity Pool
    IdentityPoolRoleMapping:
      Type: "AWS::Cognito::IdentityPoolRoleAttachment"
      Properties:
        IdentityPoolId: 
          Ref: IdentityPool
        Roles:
          authenticated:
              'Fn::GetAtt': [ CognitoAuthorizedRole, Arn ]
          unauthenticated:
              'Fn::GetAtt': [ CognitoUnAuthorizedRole, Arn ]

  Outputs:
    UserPoolId:
      Value: 
        Ref: UserPool
      Export:
        Name: "UserPool::Id"
    UserPoolClientId:
      Value:
        Ref: UserPoolClient
      Export:
        Name: "UserPoolClient::Id"
    IdentityPoolId:
      Value:
        Ref: IdentityPool
      Export:
        Name: "IdentityPool::Id"

#15

Thanks @jonsmirl - your example has been extremely helpful to me. Any chance you could tell me what I’d need to change to configure the User Pool resource so that it is set up to use the user’s email address as their “username”. I can do this via the AWS console but I want to put this config in serverless.yml.


#16

I think you remove the ‘name’ field. But does amplify support that yet? I don’t think they have gotten around to making the userpool stuff configurable yet.


#17

I don’t really know much about amplify. I just want to add the config for my user pool to my serverless service and I was able to take what I needed from your example and use that to create a user pool successfully, just not with the exact config I need.

The ‘name’ field is different to ‘username’ so I don’t think removing that will achieve what I want. Are you able to point me in direction of any docs/tutorials that might help me out? Thanks again!


#18

Use the UI to get it the way you like it. Then use the CLI to describe it.

aws cognito-idp describe-user-pool --user-pool-id xxxxxxx


#19

Ah ha! Didn’t know I could do that. Thanks, that’s super helpful.


#20

hi all
so i want use cognito pool id use in lambda function.

i try to import UserPool::Id to env varible and then use env varible inside lambda. but its not work
got a error :circuiler dependency error within serverless deploy