Leveraging AWS Cognito Identity Pool with Twitter & Google a

June 1, 2023

Introduction

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:

  1. Understanding AWS Cognito Identity Pool.
  2. The Architectural Flow.
  3. Utilizing AWS SAM for resource configuration.
  4. Deploying the resources on the AWS platform.
  5. 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.

Serverless Handbook
Access free book

The dream team

At Serverless Guru, we're a collective of proactive solution finders. We prioritize genuineness, forward-thinking vision, and above all, we commit to diligently serving our members each and every day.

See open positions

Looking for skilled architects & developers?

Join businesses around the globe that trust our services. Let's start your serverless journey. Get in touch today!
Ryan Jones - Founder
Ryan Jones
Founder
Speak to a Guru
arrow
Edu Marcos
Chief Technology Officer
Speak to a Guru
arrow
Mason Toberny
Mason Toberny
Head of Enterprise Accounts
Speak to a Guru
arrow

Join the Community

Gather, share, and learn about AWS and serverless with enthusiasts worldwide in our open and free community.