Instantly share code, notes, and snippets.

Embed
What would you like to do?
Create a Cognito Authentication Backend via CloudFormation
AWSTemplateFormatVersion: '2010-09-09'
Description: Cognito Stack
Parameters:
AuthName:
Type: String
Description: Unique Auth Name for Cognito Resources
Resources:
# Creates a role that allows Cognito to send SNS messages
SNSRole:
Type: "AWS::IAM::Role"
Properties:
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: "Allow"
Principal:
Service:
- "cognito-idp.amazonaws.com"
Action:
- "sts:AssumeRole"
Policies:
- PolicyName: "CognitoSNSPolicy"
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: "Allow"
Action: "sns:publish"
Resource: "*"
# Creates a user pool in cognito for your app to auth against
# This example requires MFA and validates the phone number to use as MFA
# Other fields can be added to the schema
UserPool:
Type: "AWS::Cognito::UserPool"
Properties:
UserPoolName: !Sub ${AuthName}-user-pool
AutoVerifiedAttributes:
- phone_number
MfaConfiguration: "ON"
SmsConfiguration:
ExternalId: !Sub ${AuthName}-external
SnsCallerArn: !GetAtt SNSRole.Arn
Schema:
- Name: name
AttributeDataType: String
Mutable: true
Required: true
- Name: email
AttributeDataType: String
Mutable: false
Required: true
- Name: phone_number
AttributeDataType: String
Mutable: false
Required: true
- Name: slackId
AttributeDataType: String
Mutable: true
# Creates a User Pool Client to be used by the identity pool
UserPoolClient:
Type: "AWS::Cognito::UserPoolClient"
Properties:
ClientName: !Sub ${AuthName}-client
GenerateSecret: false
UserPoolId: !Ref UserPool
# Creates a federeated Identity pool
IdentityPool:
Type: "AWS::Cognito::IdentityPool"
Properties:
IdentityPoolName: !Sub ${AuthName}Identity
AllowUnauthenticatedIdentities: true
CognitoIdentityProviders:
- ClientId: !Ref UserPoolClient
ProviderName: !GetAtt UserPool.ProviderName
# 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: !GetAtt CognitoAuthorizedRole.Arn
unauthenticated: !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"
@tommelo

This comment has been minimized.

Copy link

tommelo commented Aug 2, 2017

Hey @singledigit that helped a lot!
Quick question: Do you know how to allow users to use an email address or phone number as their “username” to sign up and sign in using through Cloudformation template?

cognito

@pedrogryzinsky

This comment has been minimized.

Copy link

pedrogryzinsky commented Aug 23, 2017

Hey @tommelo, i don't think it's possible in the moment, at least not through the official AWS::Cognito::UserPool resource type. AWS cfn documentation actually describes two properties available for User Pool resources called AliasAttributes and AutoVerifiedAttributes, which are related to Username sign-in strategy and administrative management. We're trying to find a custom template which implements username attributes, which is available through aws-cli using create-user-pool with --username-attributes and CreateUserPool Api through UsernameAttributes.

In the meanwhile we're using aws-cli to build our User Pools. I think this may be enough in the general case. After deployment it's unlikely for you to change your user pool sign-in / sign-up strategy, so I think this approach may solve our problem right now! :)

@trabur

This comment has been minimized.

Copy link

trabur commented Aug 23, 2017

@singledigit How could one get this to work with email verification instead of text message?

@heraldsmit

This comment has been minimized.

Copy link

heraldsmit commented Aug 25, 2017

You could try creating the pool you want manually, then print out a JSON spec of it to describe the pool using Amazon's https://github.com/awslabs/aws-shell or perhaps aws-cli, not sure about aws-cli.

screen shot 2017-08-25 at 2 25 10 pm

You can then convert the JSON to YAML to see the differences more clearly.

Or to import the spec in order to create a new pool, you need to remove the properties like DateCreated, PoolID, etc which are specific to the previous pool. The aws-shell command is:

screen shot 2017-08-25 at 2 36 20 pm

Here is a link to docs

Hope this helps.

@bluepeter

This comment has been minimized.

Copy link

bluepeter commented Nov 30, 2017

I am having the same problem. Thanks @heraldsmit for your suggestion. Unfortunately, it appears Cognito and CloudFormation just don't mix... or at least, it's not possible to create a Cognito with email as the username. In the AWS console, I created two Cognito User Pools that were exactly the same apart from the manual selection of email as the login option. Then, I ran cognito-idp and diffed the output. Nothing of consequence:

$ aws --profile tyger cognito-idp describe-user-pool --user-pool-id us-west-2_U43IvMWv4 > a.json
$ aws --profile tyger cognito-idp describe-user-pool --user-pool-id us-west-2_bA5VHNFpz > b.json
$ diff a.json b.json 
122c122
<                 "Required": true, 
---
>                 "Required": false, 
219c219
<         "Name": "sdcvsdf", 
---
>         "Name": "zdf", 
222c222
<         "LastModifiedDate": 1512023600.734, 
---
>         "LastModifiedDate": 1512023676.889, 
246c246
<         "CreationDate": 1512023600.041, 
---
>         "CreationDate": 1512023675.659, 
248c248
<         "Id": "us-west-2_U43IvMWv4", 
---
>         "Id": "us-west-2_bA5VHNFpz", 

The Required: true simply refers to the fact that, by default, when the "username" option is selected, the email address as a standard attribute is checked. When you select to user email address as the login, it gets unchecked. This has no consequence on the login choice.

@bluepeter

This comment has been minimized.

Copy link

bluepeter commented Nov 30, 2017

Note per this AWS thread it is "coming soon" to CloudFormation (but that was back in July, so who knows): https://forums.aws.amazon.com/thread.jspa?threadID=259349&tstart=0

@bsdkurt

This comment has been minimized.

Copy link

bsdkurt commented Feb 28, 2018

Hey @tommelo. You can do this using a custom resource that uses a lamba. Here is a working example:

https://github.com/bsdkurt/aws-node-custom-user-pool

@ldgarcia

This comment has been minimized.

Copy link

ldgarcia commented Mar 26, 2018

@trabur, like this:

  UserPool:
    Type: 'AWS::Cognito::UserPool'
    Properties:
      AutoVerifiedAttributes:
      - email
@ldgarcia

This comment has been minimized.

Copy link

ldgarcia commented Mar 26, 2018

@tommelo I think this should work:

  UserPool:
    Type: 'AWS::Cognito::UserPool'
    Properties:
      AliasAttributes:
      - email
      - phone_number
@illuminatedspace

This comment has been minimized.

Copy link

illuminatedspace commented Jun 7, 2018

This gist is a miracle. Thanks so much.

@mtinra

This comment has been minimized.

Copy link

mtinra commented Jun 13, 2018

Finally, they update the "use an email address or phone number as their “username”"
now you can use:

UsernameAttributes: [email]
@rhysmccaig

This comment has been minimized.

Copy link

rhysmccaig commented Jun 18, 2018

Great gist, thanks!

@MustSeeMelons

This comment has been minimized.

Copy link

MustSeeMelons commented Jul 15, 2018

Great gist, one question though, how could I add FB/Google login options on top of this?
I have added them to the IdentityPool, created seperate auth/unauth roles for each, but cant seem to figure out how to properly configure the role attachment.

@nodox

This comment has been minimized.

Copy link

nodox commented Aug 22, 2018

+1. How do you get this working with Fb/Google?

@thrixton

This comment has been minimized.

Copy link

thrixton commented Sep 26, 2018

@MustSeeMelons, @nodox, see the SO answer List of CloudFormation CognitoEvents for Cognito Identity Pool creation and the elements under "SupportedLoginProviders", worked for me.

@frankleonrose

This comment has been minimized.

Copy link

frankleonrose commented Nov 5, 2018

I can successfully create a UserPool, UserPoolGroup, and UserPoolUser in my CloudFormation stack. However, I have been unable to put the user in the group with a UserPoolUserToGroupAttachment resource. @singledigit, have you had any luck with that?

  DashboardAdminGroupAssignment:
    DependsOn:
      - DashboardUserPool (UserPool created in stack)
      - DashboardAdminGroup (UserPoolGroup created in stack with name 'Administrators')
      - DashboardAdministrator (UserPoolUser created in stack with email address DashboardAdminEmail)
    Type: AWS::Cognito::UserPoolUserToGroupAttachment
    Properties:
      UserPoolId: !Ref DashboardUserPool
      GroupName: Administrators
      Username: "6cec517c-8888-4973-9999-60e725379121" (Works! However, not useful when the user was created in the same stack and the sub would be unknown aforehand.)
      # Username: !Ref DashboardAdministrator (yields error: Unknown User)
      # Username: !Sub ${DashboardAdminEmail} (yields error: Unknown User)
      # Username: !GetAtt DashboardAdministrator.sub (yields error: Unknown User)
@jamesgraham

This comment has been minimized.

Copy link

jamesgraham commented Feb 12, 2019

@frankleonrose did you manage to work that one out?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment