Cognito User Pool and Identity Pool - Authentication and Authorization

2024年08月16日


In this article, I will talk about Cognito user pool and Cognito identity pool. The differences in their purpose. Then I will very simply configure Cognito user pool and identity pool for demonstration. In the end, I will use a few Python code to dive a little deep into Cognito concepts.


Introduction

Amazon Cognito provides two main components for managing user authentication and access control in your applications: Cognito user pool and Cognito identity pool. Each serves a different purpose:

1. Cognito User Pool

Purpose:
A Cognito user pool is primarily used for user authentication. It allows you to manage user sign-up, sign-in, and access to your applications.

Key Features:
- User Registration and Authentication: Users can sign up directly to the user pool or authenticate using social identity providers (like Google, Facebook, etc.) or SAML-based providers. In the next section of arcticle, I will demonstrate how to create local users, and use them as Cognito App Client identity provider.
- Token Generation: After successful authentication, the user pool generates JSON Web Tokens (JWT) - ID tokens, access tokens, and refresh tokens - which can be used to authenticate and authorize users in your application.
- Customizable Authentication Flow: You can customize the authentication flow with Lambda triggers to enforce custom business logic, such as multi-factor authentication (MFA), validation, and more.
- User Management: Cognito user pool comes with built-in user management features, including profile management, account recovery, and password reset.

Typical Use Case:
Use Cognito User Pools when you need to manage user authentication for your web or mobile applications. It's ideal for scenarios where you want to allow users to sign up and sign in to your application with credentials stored in Cognito or via federated identity providers.

2. Cognito Identity Pool

Purpose:
A Cognito identity pool is used for granting temporary AWS credentials to users, allowing them to access AWS services. It primarily provides users with temporary, limited-privilege access to AWS resources.

Key Features:
- Federated Identities: Cognito identity pool allows users to authenticate through multiple identity providers (including Cognito user pool, social identity providers, or even unauthenticated access).
- AWS Credentials: After successful authentication, the identity pool provides temporary AWS credentials (IAM roles) that can be used to interact with AWS services like S3, DynamoDB, or Lambda.
- Role-based Access Control: You can assign different IAM roles to different groups of users based on their authentication status (authenticated or unauthenticated) or by their identity provider.

Typical Use Case:
Use identity pool when you need to provide users with access to AWS resources. For example, if you have a mobile app that needs to upload photos to an S3 bucket or access DynamoDB tables, you would use an identity pool to grant those users temporary AWS credentials.

Combining Both
In many scenarios, both Cognito User Pools and Identity Pools are used together:
- Authentication: Use the user pool for user sign-up and sign-in.
- AWS Access: Use the identity pool to grant users access to AWS resources after they've authenticated.

This approach allows for secure and scalable management of user identities and access control across both your application and AWS resources.


Architecture


Architecture of the demo



Configuration

Create local user in user pool:


The "Cognito user pool" here corresponds to the users created inside Cognito user pool.


Creare Cognito domain. Wait a moment for the DNS record propogate.


Update the authenticated role in the identity pool by adding the following policy:
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "s3:ListAllMyBuckets"
            ],
            "Resource": "*"
        }
    ]
}



After configuration complete, access hosted UI by clicking the "View Hosted UI" button.





What's Inside

Lists the user pools associated with an AWS account.
cognito_idp_client = boto3.client("cognito-idp")
user_pools = cognito_idp_client.list_user_pools(MaxResults=50)["UserPools"]
Sample data of user_pools:
[{'CreationDate': datetime.datetime(2024, 8, 15, 3, 11, 56, 791000, tzinfo=tzlocal()),
'Id': 'us-west-2_K****C',
'LambdaConfig': {},
'LastModifiedDate': datetime.datetime(2024, 8, 15, 3, 11, 56, 791000, tzinfo=tzlocal()),
'Name': 'test'}]

Because I only created one user pool, for simplicity, let's list the clients that have been created for the specified user pool.
user_pool = user_pools[0]
client_ids = cognito_idp_client.list_user_pool_clients(UserPoolId=user_pool["Id"])["UserPoolClients"]
Sample data of client_ids.
[{'ClientId': '7g****co',
'ClientName': 'test',
'UserPoolId': 'us-west-2_K***C'}]

Because I created only one app client inside the user pool, for simplicity, let's see the app client details of the configuration information and metadata of the specified user pool app client.
app_client_id = client_ids[0]["ClientId"]
app_client_details = cognito_idp_client.describe_user_pool_client(UserPoolId=user_pool["Id"], ClientId=app_client_id)
Sample data of app_client_details.
{'ResponseMetadata': { ... },
'UserPoolClient': {'AccessTokenValidity': 60,
'AllowedOAuthFlows': ['code'],
'AllowedOAuthFlowsUserPoolClient': True,
'AllowedOAuthScopes': ['email', 'openid'],
'AuthSessionValidity': 3,
'CallbackURLs': ['https://localhost'],
'ClientId': '7g****co',
'ClientName': 'test',
'CreationDate': datetime.datetime(2024, 8, 15, 3, 11, 57, 248000, tzinfo=tzlocal()),
'EnablePropagateAdditionalUserContextData': False,
'EnableTokenRevocation': True,
'ExplicitAuthFlows': ['ALLOW_REFRESH_TOKEN_AUTH',
'ALLOW_USER_PASSWORD_AUTH'],
'IdTokenValidity': 60,
'LastModifiedDate': datetime.datetime(2024, 8, 15, 3, 54, 55, 337000, tzinfo=tzlocal()),
'PreventUserExistenceErrors': 'ENABLED',
'ReadAttributes': ['address',
'birthdate',
'email',
'email_verified',
'family_name',
'gender',
'given_name',
'locale',
'middle_name',
'name',
'nickname',
'phone_number',
'phone_number_verified',
'picture',
'preferred_username',
'profile',
'updated_at',
'website',
'zoneinfo'],
'RefreshTokenValidity': 30,
'SupportedIdentityProviders': ['COGNITO'],
'TokenValidityUnits': {'AccessToken': 'minutes',
'IdToken': 'minutes',
'RefreshToken': 'days'},
'UserPoolId': 'us-west-2_K***C',
'WriteAttributes': ['address',
'birthdate',
'email',
'family_name',
'gender',
'given_name',
'locale',
'middle_name',
'name',
'nickname',
'phone_number',
'picture',
'preferred_username',
'profile',
'updated_at',
'website',
'zoneinfo']}}
Notice that the value of app_client_details["UserPoolClient"]["SupportedIdentityProviders"] is ['COGNITO'].

Let's list information about all IdPs for a user pool.
response = cognito_idp_client.list_identity_providers(UserPoolId=user_pool["Id"])['Providers']
Sample data of response:
[]

Now let's try to get information about this IdP.
response = cognito_idp_client.describe_identity_provider(UserPoolId=user_pool["Id"], ProviderName=supported_providers[0])
In this scenario, it is expected to fail and you will see an error message similar to the following one:

{
  "errorMessage": "An error occurred (ResourceNotFoundException) when calling the DescribeIdentityProvider operation: Identity provider COGNITO for tenantId us-west-2_K***C does not exist.",
  "errorType": "ResourceNotFoundException",
  "requestId": "...",
  "stackTrace": [
    ...
  ]
}


Demo Code

Step 1. Define constants of Cognito user pool, app client, and identity pool.
import json
import logging

import boto3
import requests


USER_POOL_ID = 'us-west-2_K***C'
CLIENT_ID = '7g****co'
IDENTITY_POOL_ID = 'us-west-2:55588085-****-****-****-c7537ce48973'
REGION = 'us-west-2'

cognito_idp_client = boto3.client('cognito-idp', region_name=REGION)
cognito_identity_client = boto3.client('cognito-identity', region_name=REGION)

logger = logging.getLogger()
logger.setLevel(logging.INFO)
...

Step 2. Authenticate the user using Cognito user pool.
...
def authenticate_user():
    response = cognito_idp_client.initiate_auth(
        AuthFlow='USER_PASSWORD_AUTH',
        AuthParameters={
            'USERNAME': USERNAME,
            'PASSWORD': PASSWORD
        },
        ClientId=CLIENT_ID
    )
    return response['AuthenticationResult']['IdToken']
...
id_token = authenticate_user()
...

Step 3. Use the ID token to get AWS credentials from the Cognito identity pool.
...
def get_aws_credentials(id_token):
    identity_id = client.get_id(
        IdentityPoolId=IDENTITY_POOL_ID,
        Logins={
            f'cognito-idp.{REGION}.amazonaws.com/{USER_POOL_ID}': id_token
        }
    )['IdentityId']
    
    # AWS credentials
    return = client.get_credentials_for_identity(
        IdentityId=identity_id,
        Logins={
            f'cognito-idp.{REGION}.amazonaws.com/{USER_POOL_ID}': id_token
        }
    )['Credentials']
...
credentials = get_aws_credentials(id_token)
...

Step 4. Use the AWS credentials to access S3 or any other AWS service.
...
def access_s3(credentials):
    s3_client = boto3.client(
        's3',
        aws_access_key_id=credentials['AccessKeyId'],
        aws_secret_access_key=credentials['SecretKey'],
        aws_session_token=credentials['SessionToken'],
        region_name=REGION
    )
    
    # Example: List all buckets in S3
    response = s3_client.list_buckets()
    for bucket in response['Buckets']:
        print(f'Bucket Name: {bucket["Name"]}')
...
access_s3(credentials)
...
Output:
...
... Bucket Name: COGNITO-DEMO-EXAMPLE
...


Conclusion

In this article, I first explained some of the main differences between the two main Cognito components, i.e., user pool and identity pool. Then, I listed the main steps to configure user pool and identity pool to proceed to the next step. Next, I used boto3 library to dive a little deep into Cognito settings. In the last section of this article, I give a simple example of how Cognito could be used for authN and authZ, and finally access AWS resources using a temporary token.

This is a brief introduction of Cognito. The configuration and code used in this post in only for demonstration. In production, they need to be adapted and optimized, e.g., iteration and etc.

I hope this helps and hope you like it.


References


CognitoIdentityProvider


Category: AWS Tags: public

Upvote


Downvote