A Detailed Look at AWS Cognito

Access control is often a significant challenge in every application. We use access control to identify who the user is, grant access to the information they are allowed to see and prevent access to everything else.

AWS Cognito: The What and Why

I have pretty much explained what AWS Cognito is; a reliable, scalable, user sign-up and authentication service. AWS Cognito ties itself to an authentication directory, where user account data is stored. Alternatively, you can federate AWS Cognito so the actual authentication is performed by social media authentication engines like Google, Facebook, and Amazon, or by a Security Assertion Markup Language (SAML) compliant enterprise authentication service or an existing Microsoft Active Directory.

  • Social and Enterprise Identify Federation — The ability to connect the user authentication to services such as Google, Facebook, Amazon or an enterprise service, removing the need to maintain the user authentication data outside of the federated relationship.
  • Standards Compliant — AWS Cognito supports OAuth 2.0, SAML and OpenID Connect authentication standards, so you don’t have to implement these in your solution.
  • Security for your applications and users — AWS Cognito supports multi-factor authentication (MFA), and encryption of data both in-transit and in-storage. AWS Cognito is HIPAA eligible and PCI DSS compliant.
  • Access Control for AWS Resources — Whatever resources you create in AWS, access to those resources can be restricted based upon the roles and user mapping you provide for that resource.
  • Customizable UI and Easy Integration — AWS Cognito has a customizable UI, so you don’t even have to write the code to collect the user’s credentials — you can use and customize the one provided as part of AWS Cognito. Integration within your application can be done in minutes.
  • IOS Swift applications;
  • Android applications;
  • REACT native applications and,
  • Web applications.

Setting Up AWS Cognito

In this section, we are going to look at configuring a user pool. After logging in to the console and navigating to the AWS Cognito, we have this view.

Password Policies

The second step is to define the password policy. If you want to allow users to be able to sign themselves up, and how quickly temporary passwords should expire.

Multi-Factor Authentication

The next step is to configure multi-factor (MFA) or two-step authentication. This option provides better security for the end users, as it requires a second piece of information — the security challenge — to be entered to log in. The security challenge is sent via Short Message Service (SMS) to the user, who must provide the security code to complete the login.

Email Configuration

If AWS Cognito will be sending email messages on your behalf, you may need to configure AWS Simple Email Service. AWS Cognito is limited to 50 email messages per day for user pool, and 500 email messages per day per AWS account. If you anticipate exceeding these hard limits, you will need to configure Simple Email Service.

Devices

AWS Cognito can track the devices user’s access. You can opt to always track every device the user logs in from; opt-in, where the user can choose to remember the device they are logging in from, or never track the device. Any of the options are valid and the decision is yours,

App Clients

Every application wishing to authenticate through the user pool must have the pool ID, a unique ID for the application and an optional secret key. When the user pool is being created, we wouldn’t normally add an app client.

Triggers

Triggers allow more advanced configurations using AWS Lambda functions to provide additional, dynamic actions when the trigger is executed. Your user pool may never have any of these triggers defined.

Review

After you have completed all of the configuration, the “Review” page is where you validate your responses and click “Create” to deploy the user pool. Once created, a message is displayed indicating the user pool has been created.

Users and Groups

With the user pool created, a new option is shown on the navigation menu allowing us to use the console to create groups, create users and associate users with groups.

Adding a Group

For this example, we are going to add two groups to our user pool. After selecting Groups and clicking on “Create Group”, we are prompted to provide the group name, a description, select a role, and set a precedence value.

Adding a User

The process to add a user follows a similar process as adding a group, but the workflow after the user is added differs.

A Sample Implementation

Now that we have our user pool created, let’s look at using the AWS SDK to interact with the user pool.

List Users

We may want to list the users in the pool. This sample function illustrates listing the users in the sample “Testme” pool created earlier in the article.

import boto3
import os
import datetime
import json
import time
__POOL_ID__ = "pool_id"

def main():
global __POOL_ID__

client = boto3.client('cognito-idp')
response = client.list_users(
UserPoolId=__POOL_ID__
)
print(response)
return
if __name__ == "__main__":
main()
$ python3 listuser.py
{
'Users': [
{
'Username': 'ea6bca08-156e-4c8a-8338-f7fc7ccba16e',
'Attributes': [
{
'Name': 'sub',
'Value': 'ea6bca08-156e-4c8a-8338-f7fc7ccba16e'
},
{
'Name': 'email_verified',
'Value': 'true'
},
{
'Name': 'phone_number_verified',
'Value': 'true'
},
{
'Name': 'phone_number',
'Value': '+1903npaXXXX'
},
{ 'Name': 'email',
'Value': 'user@example.com'
}
],
'UserCreateDate': datetime.datetime(2019, 10, 24, 8, 56, 17, 922000, tzinfo=tzlocal()),
'UserLastModifiedDate': datetime.datetime(2019, 10, 24, 8, 56, 17, 922000, tzinfo=tzlocal()),
'Enabled': True,
'UserStatus': 'FORCE_CHANGE_PASSWORD'}
],
'ResponseMetadata':
{
'RequestId': '565cf314-4a07-4271-895f-984f34f41d34',
'HTTPStatusCode': 200,
'HTTPHeaders':
{
'date': 'Thu, 24 Oct 2019 21:25:23 GMT',
'content-type': 'application/x-amz-json-1.1',
'content-length': '444', 'connection': 'keep-alive',
'x-amzn-requestid': '565cf314-4a07-4271-895f-984f34f41d34'},
'RetryAttempts': 0
}
}

Adding Bulk Users

Whether we want to add one user or a thousand, automation is important. It is easy to assume that you create a user with the standard attributes identified when you create the pool, when in fact you do not. No additional information other than what is needed to create the user in the console is needed when using the admin_create_user API call.

client = boto3.client('cognito-idp')

response = client.admin_create_user(
UserPoolId=__POOL_ID__,
Username=userData['email'],
UserAttributes=[
{
'Name': 'phone_number',
'Value': userData['phone']
},
{
'Name': 'email',
'Value': userData['email']
},
{
'Name': 'email_verified',
'Value': 'True'
},
{
'Name': 'phone_number_verified',
'Value': 'True'
}
],
ForceAliasCreation=True,
DesiredDeliveryMediums=[
'EMAIL',
]
)

Updating user attributes

Once the user account has been added, we can add additional standard and custom attributes to the user record. In the code sample below, we have added user attributes for name, first name, middle name, given name, birthdate, address, and gender. Adding these attributes to the user account requires using the admin_update_user_attributes API call, which needs the user pool ID and the username the attributes should be applied to.

response = client.admin_update_user_attributes(
UserPoolId=__POOL_ID__,
Username=userData['email'],
UserAttributes=[
{
'Name': 'birthdate',
'Value': userData['birthdate'],
},
{
'Name': 'address',
'Value': userData['address'],
},
{
'Name': 'family_name',
'Value': userData['family_name'],
},
{
'Name': 'given_name',
'Value': userData['given_name'],
},
{
'Name': 'middle_name',
'Value': userData['middle_name'],
},
{
'Name': 'name',
'Value': userData['name'],
},
{
'Name': 'gender',
'Value': userData['gender'],
}
]
)

Add users to Groups

We can extend our sample program to also add the user to a group if one is specified in the CSV source file.

if userData['group'] is not None:
response = client.admin_add_user_to_group(
UserPoolId=__POOL_ID__,
Username=userData['email'],
GroupName=userData['group']
)

An Important Note about Removing Users

If you want to remove a user from your user pool through the console, you must first disable the user account. Then and only then is it possible to delete the account. This same restriction does not apply to the API. It may be desirable, however, to disable access for a user before deleting the user account.

Cognito Sync

I am mentioning AWS Cognito Sync here only because some of the current AWS Cognito documentation still references it. AWS Cognito sync provides a mechanism for synchronizing application-related user data across devices. If you need this service, check out AWS AppSync which provides this capability and more.

Automation

Like many things in AWS, Cognito User and Identity Pools can be created using CloudFormation. Indeed, if you are going to deploy AWS Cognito, it may be better to define the CloudFormation to create the pool, as it is entirely possible you won’t get the pool created to meet your needs on the first attempt. Additionally, if you create one AWS Cognito pool, it is possible you may need another, so again, defining the CloudFormation first.

  • group management; and,
  • inactivating users who have not logged in.

Pricing

One of the best features of AWS is paying for what you use. This is the same for AWS Cognito, in that you pay for the number of Monthly Active Users (MAUs), and not the total configured users in the directory. The price varies based upon volume. An MAU is a user who has signed-in, signed-up, refreshed their authentication token or changed their password.

Recommendation

Like all AWS services, as the service is being designed, the rationale for making various design and architecture decisions should be documented. By creating and maintaining the documentation and decisions, when at some future point you wonder why someone made the decision they did, there is an explanation as to why. This is very important for services like AWS Cognito, where many of the decisions are immutable.

Conclusion

To User Pool or Federate, that is the question.

References

AWS AppSync

About the Author

Chris is a highly-skilled Information Technology AWS Cloud, Training and Security Professional bringing cloud, security, training and process engineering leadership to simplify and deliver high-quality products. He is the co-author of more than seven books and author of more than 70 articles and book chapters in technical, management and information security publications. His extensive technology, information security, and training experience makes him a key resource who can help companies through technical challenges.

Copyright

This article is Copyright © 2019, Chris Hare.

Chris is the co-author of seven books and author of more than 70 articles and book chapters in technical, management, and information security publications.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store