Looking for Senior AWS Serverless Architects & Engineers?
Let's TalkIntroduction
In this article, we will understand how we can leverage AWS Cognito Identity Pool to authenticate and authorize our backend resources with the help of Twitter and Google as our Identity Providers via AWS SAM.
Architecture overview
Implementation
Pre-requisites
- AWS Account, free tier should be fine.
- Familiarity with AWS SAM
Steps:
- Understanding AWS Cognito Identity Pool.
- The Architectural Flow.
- Utilizing AWS SAM for resource configuration.
- Deploying the resources on the AWS platform.
- Conducting tests and reviewing the deployed resources via the AWS Console.
Step 1 - Understanding AWS Cognito Identity Pool:
Let’s break down how AWS Cognito Identity Pool works behind the scenes. But first, before we do that, we have to understand what Cognito Identity Pool is and when to use it.
💡 What is AWS Cognito Identity Pool and when to use it? 🤔
AWS Cognito identity pool, enables you to grant temporary, limited-privilege credentials such as AWS access keys, to users who authenticate through various third-party identity providers (IdPs) like Google, Amazon, Twitter, Facebook, etc., or even openID, thus providing secure access to AWS resources based on the permissions defined by 'IAM roles'.
Using Cognito Identity Pool is highly advantageous when you desire password-less security for your applications and prefer your users to authenticate or log in through social providers.
In addition, consider a scenario where not all users need to authenticate into your application, such as guest users. AWS Cognito Identity Pool provides features to enable guest users to access AWS resources without authentication. The best part is that you have complete control over the IAM permissions you grant to both guest users and authenticated users. That's so cool, isn’t it? 😎
Step 2 - The Architectural Flow:
Now, let's proceed to the 'architecture flow' for a clearer understanding. To facilitate this, I have designed a step-by-step architecture. Let's begin by exploring the first step of the architecture:
- When a user attempts to sign in to the application using social providers like Twitter, Facebook, and Cognito User Pool as Federated Identity Providers (IDPs), the flow follows these steps. For example, if a user chooses to sign in with Twitter, the Twitter SDK validates the user's authentication and, upon successful validation, provides a 'JWT' token as a response along with other attributes.
- Within the Cognito Identity Pool console, we configure the Twitter Client Id and Client Secret obtained from the Twitter developer console. The Identity Pool then validates the JWT token. If the token is valid, it moves on to Step 3. However, if the token is invalid, an error response is triggered, rolling back the flow.
- Once, Identity Pool Validates the token, it’s assumed that the user is valid and it will generate a temporary AWS credential for the user using web identity federation support in the AWS Security Token Service (AWS STS). These temporary credentials are associated with a specific IAM role that is defined in the Cognito Identity Pool console as 'Authenticated role'.
- Once you acquire the temporary AWS credentials (accessKeyId, secretAccessKey, and sessionToken), you can utilize these keys to access various AWS resources, such as APIGateway.
- Also, to retain these keys for future access to your backend AWS resources, you can store them in the browser using mechanisms such as 'local storage' or 'cookies'.
Step 3 - AWS SAM Configuration:
In this section, we will explore the configuration of the Cognito Identity Pool in the AWS Serverless Application Model (SAM). The configuration process is divided into phases, making it easier to understand and follow. 🙂
AWSTemplateFormatVersion: '2010-09-09'
Description: Cognito Identity Pool Stack
Transform: 'AWS::Serverless-2016-10-31'
Resources:
#********************** Phase1 **********************
ApiGatewayApi:
Type: AWS::Serverless::Api
Properties:
StageName: dev
Name: BookStore_API
Cors:
AllowMethods: "'PUT', 'POST'"
AllowHeaders: "'*'"
AllowOrigin: "'*'"
DynamoDBTable:
Type: AWS::DynamoDB::Table
Properties:
TableName: Book_Table
BillingMode: PAY_PER_REQUEST
AttributeDefinitions:
- AttributeName: book_id
AttributeType: "N"
KeySchema:
- AttributeName: book_id
KeyType: HASH
#********************** Phase2 **********************
IdentityPool:
Type: "AWS::Cognito::IdentityPool"
Properties:
IdentityPoolName: book_store_app
AllowUnauthenticatedIdentities: true
SupportedLoginProviders:
accounts.google.com: client_id
api.twitter.com: client_id;client_secret
#********************** Phase3 **********************
CognitoAuthorizedRole:
Type: "AWS::IAM::Role"
Properties:
RoleName: "Cognito_book_store_appAuth_Role"
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:
- "cognito-identity:*"
- "execute-api:Invoke"
Resource: !Sub arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${ApiGatewayApi}/*
#********************** Phase4 **********************
CognitoUnAuthorizedRole:
Type: "AWS::IAM::Role"
Properties:
RoleName: "Cognito_book_store_appUnauth_Role"
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:
- "dynamodb:GetItem"
- "dynamodb:Scan"
Resource: !GetAtt DynamoDBTable.Arn
#********************** Phase5 **********************
IdentityPoolRoleMapping:
Type: "AWS::Cognito::IdentityPoolRoleAttachment"
Properties:
IdentityPoolId: !Ref IdentityPool
Roles:
authenticated: !GetAtt CognitoAuthorizedRole.Arn
unauthenticated: !GetAtt CognitoUnAuthorizedRole.Arn
Get ready to dive into the configuration of Identity Pool resources using SAM. Let's begin by exploring Phase 1 in the YAML code block mentioned above.
Phase 1:
Is relatively straightforward, especially if you are familiar with using SAM to configure serverless resources. In this phase, two resources are defined: APIGateway with the name BookStore_API and a DynamoDB Table named Book_Table. These resources are intended to create IAM policies for our 'Auth' and 'Unauth' roles, which we will delve into in Phase 3 and Phase 4.
Phase 2:
In this phase, we’re going to start to configure Cognito Identity Pool.
IdentityPool:
Type: "AWS::Cognito::IdentityPool"
Properties:
IdentityPoolName: book_store_app
AllowUnauthenticatedIdentities: true
SupportedLoginProviders:
accounts.google.com: client_id
api.twitter.com: client_id;client_secret
Here, we're setting up the basic details of the Cognito Identity Pool, such as the Identity Pool name. To allow guest users access to AWS resources, the 'AllowUnauthenticatedIdentities' attribute must be set to True. For social login configuration, Google login only requires the client ID, while Twitter login requires both the Client ID and Client Secret, separated by a semicolon ';'
Phase 3:
In this section, we’re going to configure Authenticate role for our Cognito IDP.
CognitoAuthorizedRole:
Type: "AWS::IAM::Role"
Properties:
RoleName: "Cognito_book_store_appAuth_Role"
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:
- "cognito-identity:*"
- "execute-api:Invoke"
Resource: !Sub arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${ApiGatewayApi}/*
To enable user authentication and access to your backend resources, it is crucial to handle this configuration with caution. The key aspect is configuring the 'AssumeRolePolicyDocument' property, which allows obtaining temporary credentials from STS through web identity federation support in AWS. Also, 'cognito-identity.amazonaws.com:amr' this attribute must be set to authenticated. Additionally, I configured a policy to invoke an API Gateway for our use case, ensuring authenticated users can access it. Remember to set AWS_IAM as the authorization method in your API Gateway console.
Phase 4:
In this phase, we’re looking into how we configure the unAuth role for our guest users.
CognitoUnAuthorizedRole:
Type: "AWS::IAM::Role"
Properties:
RoleName: "Cognito_book_store_appUnauth_Role"
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:
- "dynamodb:GetItem"
- "dynamodb:Scan"
Resource: !GetAtt DynamoDBTable.Arn
This phase is straightforward and follows a similar configuration as phase 3. In order to grant guest users access to AWS resources, you need to set the attribute 'cognito-identity.amazonaws.com:amr' as unauthenticated. Furthermore, I have specifically configured the policy for DynamoDB to allow guest users only the "Get" and "Scan" operations.
Phase 5:
In this particular section, we are straightforwardly associating the authorized and unauthorized roles with our Identity Pool Id.
IdentityPoolRoleMapping:
Type: "AWS::Cognito::IdentityPoolRoleAttachment"
Properties:
IdentityPoolId: !Ref IdentityPool
Roles:
authenticated: !GetAtt CognitoAuthorizedRole.Arn
unauthenticated: !GetAtt CognitoUnAuthorizedRole.Arn
Step 4 - Deploying Resources to AWS.
With everything in place and our resources configured using SAM, it is now time to deploy our resources to the Cloud. To accomplish this task, I assume you have already installed the SAM CLI on your system and are familiar with it. If you are ready to proceed, simply follow these commands:
- Run this command to build your SAM resources.
sam build
- Run this to deploy the resources in the cloud.
sam deploy --stack-name "serverless-cognito-idp-stack" --resolve-s3 --capabilities CAPABILITY_IAM
Step 5 - Review and Test the deployed resources.
In order to access and review your deployed resources on the AWS Cloud, you need to log in to your AWS account. From there, navigate to the Identity Pools tab in the sidebar of the AWS Cognito Console. Next, locate and open the specific identity pool that you named during the configuration process in Phase 2.
Now, within your Identity Pool webpage, scroll down and go to the 'User access' tab. Further, scroll down, and you will come across the status for the Guest User section. It should be 'active' if you want your guest features to have access to AWS resources. Additionally, adjacent to it, you will find the name of the UnAuth Role.
Furthermore, on the same webpage, you will also discover access for authenticated users. Here, you will find a list of all the social identities you have configured. In my case, I have configured only Google and Twitter as social identities.
To conduct testing, I used a front-end application and added Twitter and Google SDKs to the client-side (React app). However, I will demonstrate how you can retrieve the credentials from Cognito after a successful login using any of the social identities.
I have written some code snippets in ReactJS that demonstrates how to exchange credentials. If you are using NodeJS as your server-side language, the code snippet is very similar. Here are the code snippets for reference.
const allowAccess = async (res, data, twitter_data) => {
if ((res && data) || twitter_data) {
var Keys = new AWS.CognitoIdentityCredentials({
IdentityPoolId: 'ap-northeast-1:b4xxxxxx', //replace with your Identity Pool ID.
Logins: {
'api.twitter.com': twitter_data ? `${twitter_data.oauth_token};${twitter_data.oauth_token_secret}` : null,
'accounts.google.com': (res && data) ? res.tokenObj.id_token : null,
}
});
Keys.get(async function () {
var tokens = {
accessKey: Keys.accessKeyId,
secretAccessKey: Keys.secretAccessKey,
sessionToken: Keys.sessionToken
};
console.log(tokens);
localStorage.setItem('tokens', JSON.stringify(tokens));
localStorage.setItem('user', data ? data.email : twitter_data.screen_name);
credentials.clearCachedId();
window.location.href = '/';
});
}
}
Bonus Tip:
💡 Although we are currently exchanging the credentials from the frontend side it's best practice to exchange these credentials from the server-side/backend.
You can find all the source codes on the GitHub Repo, which is mentioned in the Resources section below.
Conclusion
Now that you have read this article, you have an understanding of how to set up the AWS Cognito Identity Pool with Twitter and Google as Federated Identities using AWS SAM. It's worth noting that configuring these social identities is simpler compared to configuring OpenID.
In addition to Twitter and Google, you can also configure OpenID and SAML (Security Assertion Markup Language) as identity providers, but that will be covered in a separate article. 🙂Furthermore, we have learned how to obtain security credentials from Cognito after successfully verifying with one of the login providers or social identities.
Resources
Some of the resources, where I referenced while configuring Cognito Identity Pool.
- Authentication Flow Concepts: https://docs.aws.amazon.com/cognito/latest/developerguide/authentication-flow.html
- Integrating with your application:
- https://docs.aws.amazon.com/cognito/latest/developerguide/getting-credentials.html
- Google & Twitter developer portal (to get client's Ids)
- Twitter: https://developer.twitter.com/en/portal/dashboard
- Google: https://console.cloud.google.com/cloud-resource-manager
- GitHub Repo
- https://github.com/DebarshiMondal-06/Cognito-Identity-Pool
- Reference Video
- https://youtu.be/zlfMOmtVieI