Change Permission for Guest/Sign-in users in Cognito ID Pool

Change Permission for Guest/Sign-in users in Cognito ID Pool

The Cognito ID pool can generate temporary credentials for unauthenticated guest users in addition to authenticated users.
In this case, we will check a configuration that grants different privileges to guest sign-in users.

Environment

Diagram of change permissions for guest/sign-in users in Cognito ID Pool.

Create an S3 bucket and enable the static website hosting feature.
Prepare content by placing HTML files inside.

Create a sign-in function in the hosted UI of the Cognito user pool.

Configure the Cognito ID pool to assign privileges to guest sign-in users.
Specifically, create two Lambda functions and configure each to be executed by the corresponding user.

CloudFormation template files

The above configuration is built with CloudFormation.
The following URL contains the CloudFormation template, browser scripts, etc.

https://github.com/awstut-an-r/awstut-dva/tree/main/03/003

Explanation of key points in template files

To change permissions between sign-in users and guest users, we will review the Cognito ID pool, focusing on the configuration.

Attach IAM roles for guest and sign-in users to Cognito ID pool

The way to authorize users in the Cognito ID pool is to attach the corresponding IAM role.

Resources:
  IdentityPool:
    Type: AWS::Cognito::IdentityPool
    Properties:
      AllowUnauthenticatedIdentities: true
      CognitoIdentityProviders:
        - ClientId: !Ref UserPoolClient
          ProviderName: !Sub "cognito-idp.${AWS::Region}.amazonaws.com/${UserPool}"
      IdentityPoolName: !Sub "${Prefix}-IdentityPool"

  IdentityPoolRoleAttachment:
    Type: AWS::Cognito::IdentityPoolRoleAttachment
    Properties:
      IdentityPoolId: !Ref IdentityPool
      Roles:
        authenticated: !GetAtt IdentityPoolAuthenticatedRole.Arn
        unauthenticated: !GetAtt IdentityPoolUnauthenticatedRole.Arn
Code language: YAML (yaml)

The ID pool itself does not require any special configuration.
It is simply configured to work with the user pool.

The point is to attach IAM roles.
In this case, we will assign different privileges to sign-in and guest users, so we will set up IAM roles using the authenticated and unauthenticated properties, respectively.

This time we will define the following IAM roles

Resources:
  IdentityPoolAuthenticatedRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Action: sts:AssumeRoleWithWebIdentity
            Principal:
              Federated: cognito-identity.amazonaws.com
            Condition:
              StringEquals:
                cognito-identity.amazonaws.com:aud: !Ref IdentityPool
              ForAnyValue:StringLike:
                cognito-identity.amazonaws.com:amr: authenticated
      Policies:
        - PolicyName: IdentityPoolAuthenticatedPolicy
          PolicyDocument:
            Version: 2012-10-17
            Statement:
              - Effect: Allow
                Action:
                  - lambda:InvokeFunction
                Resource:
                  - !Ref AuthenticatedFunctionArn

  IdentityPoolUnauthenticatedRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Action: sts:AssumeRoleWithWebIdentity
            Principal:
              Federated: cognito-identity.amazonaws.com
            Condition:
              StringEquals:
                cognito-identity.amazonaws.com:aud: !Ref IdentityPool
              ForAnyValue:StringLike:
                cognito-identity.amazonaws.com:amr: unauthenticated
      Policies:
        - PolicyName: IdentityPoolUnauthenticatedPolicy
          PolicyDocument:
            Version: 2012-10-17
            Statement:
              - Effect: Allow
                Action:
                  - lambda:InvokeFunction
                Resource:
                  - !Ref UnauthenticatedFunctionArn
Code language: YAML (yaml)

Set the conditions for assuming the IAM role in the Condition property.
Please refer to the following page for details.

https://docs.aws.amazon.com/cognito/latest/developerguide/role-trust-and-permissions.html

In the Policies property, define the privileges to be granted.
Specify a Lambda function to allow execution.

Lambda functions to invoke

Although not related to Cognito, check the Lambda function definitions.

Resources:
  AuthenticatedFunction:
    Type: AWS::Lambda::Function
    Properties:
      FunctionName: !Sub "${Prefix}-AuthenticatedFunction"
      Runtime: python3.9
      Role: !GetAtt LambdaRole.Arn
      Handler: index.lambda_handler
      Code:
        ZipFile: |
          import datetime

          def lambda_handler(event, context):
            return str(datetime.date.today())


  UnauthenticatedFunction:
    Type: AWS::Lambda::Function
    Properties:
      FunctionName: !Sub "${Prefix}-UnauthenticatedFunction"
      Runtime: python3.9
      Role: !GetAtt LambdaRole.Arn
      Handler: index.lambda_handler
      Code:
        ZipFile: |
          import datetime

          def lambda_handler(event, context):
            return str(datetime.datetime.now().time())
Code language: YAML (yaml)

Describe the code to be executed inline.
For more information on how to define a Lambda function, please see the following page

あわせて読みたい
3 parterns to create Lambda with CloudFormation (S3/Inline/Container) 【Creating Lambda with CloudFormation】 When creating a Lambda with CloudFormation, there are three main patterns as follows. Uploading the code to an S3 buc...

Select Python 3.9 as the runtime environment and configure each to perform the following

  • Function for signed-in users: Returns today’s date.
  • Function for guest user: Returns the current time.

Browser Script

Use JavaScript to implement Cognito functionality after sign-in.
For instructions on how to create a browser script using AWS SDK for JavaScript v3, please refer to the following page.

あわせて読みたい
Using AWS SDK for JavaScript v3 in Browser 【Using AWS SDK for JavaScript v3 in Browser】 When developing Web applications using AWS, the best practice is to use the AWS SDK for JavaScript.The SDK is ...

This page will focus on content related to the sorting of guest and sign-in users.

Obtaining ID Token

In this configuration, the OAuth flow is set to Implicit Grant.
With Implicit Grant, an ID token can be obtained from the URL parameter.
In the case of a signed-in user, the following code can be used to obtain an ID token.

const params = new URLSearchParams(location.hash.slice(1));
const idToken = params.get("id_token");
Code language: JavaScript (javascript)

For more information, please see the following page.

あわせて読みたい
Authorization by Cognito ID Pool after Authentication by User Pool – Implicit Grant Ver 【Authorization by Cognito ID Pool after Authentication by User Pool - Implicit Grant Ver】 Use the Cognito user pool and identity pool to grant signed-in us...

Create Lambda client according to user

Create a client object to access the Lambda function.
The key point is to separate the responses according to the presence or absence of an ID token.

import {
  CognitoIdentityClient
} from "@aws-sdk/client-cognito-identity";

import {
  fromCognitoIdentityPool
} from "@aws-sdk/credential-provider-cognito-identity";

import {
  LambdaClient,
  InvokeCommand
} from "@aws-sdk/client-lambda";

import {
  toUtf8
} from "@aws-sdk/util-utf8-browser";

let lambdaClient;
let functionName;
if (idToken) {
  lambdaClient = new LambdaClient({
    region: REGION,
    credentials: fromCognitoIdentityPool({
      client: new CognitoIdentityClient({ region: REGION }),
      identityPoolId: IDENTITY_POOL_ID,
      logins: {
        [`cognito-idp.${REGION}.amazonaws.com/${USER_POOL_ID}`]: idToken
      }
    }),
  });
  functionName = FUNCTION_NAME_AUTHENTICATED;
} else {
  lambdaClient = new LambdaClient({
    region: REGION,
    credentials: fromCognitoIdentityPool({
      client: new CognitoIdentityClient({ region: REGION }),
      identityPoolId: IDENTITY_POOL_ID
    }),
  });
  functionName = FUNCTION_NAME_UNAUTHENTICATED;
}
Code language: JavaScript (javascript)

In the case of a signed-in user, the aforementioned code can be used to obtain an ID token.
Therefore, the client object is created using that token.

On the other hand, in the case of a guest user, the ID token cannot be obtained because the user has not signed in.
Therefore, the client object is created without the ID token.

This client object will use credentials based on the IAM role attached to the identity pool.
In other words, the privileges are set according to the user.

Lambda Function Invocation

The process after this is common.
Execute a Lambda function using the client object.

(async () => {
  const response = await lambdaClient.send(
    new InvokeCommand({ FunctionName: functionName })
  );
  document.getElementById("function-result").innerText = toUtf8(response.Payload);
})();
Code language: JavaScript (javascript)

Execute a Lambda function and embed the execution result in HTML.
As mentioned earlier, the function is executed within the scope of the privileges granted by the credential.

HTML file

HTML is also checked for reference.

<html>
  <head></head>
  <body>
    <h1>index.html</h1>
    <p id="function-result"></p>
    <ul>
      <li>
        <p><a href="https://[domain].auth.ap-northeast-1.amazoncognito.com/login?response_type=token&client_id=[client-id]&redirect_uri=[redirect-url]/index.html">Sign In</a></p>
      </li>
      <li>
        <p><a href="https://[domain].auth.ap-northeast-1.amazoncognito.com/logout?client_id=[client-id]&logout_uri=[redirect-url]/index.html">Sign Out</a></p>
      </li>
    </ul>
    <script type="text/javascript" src="./main.js"></script>
  </body>
</html>
Code language: HTML, XML (xml)

Embed the result of the Lambda function execution within a p tag with a class name of function-result.

Place a URL for sign-in and sign-out.
Set the redirect URL to point to this index.html.

Architecting

Use CloudFormation to build this environment and check actual behavior.

Create CloudFormation stacks and check resources in stacks

Create a CloudFormation stack.
For information on how to create stacks and check each stack, please refer to the following page

あわせて読みたい
CloudFormation’s nested stack 【How to build an environment with a nested CloudFormation stack】 Examine nested stacks in CloudFormation. CloudFormation allows you to nest stacks. Nested ...

Place content in S3 buckets

Place HTML and JavaScript files in the S3 bucket.

Two files are uploaded to the S3 bucket.

These two files are the contents after sign-in/sign-out.

Verification: Access as guest user

Now that we are ready, we can actually access the contents.
Access the following URL

https://s3-ap-northeast-1.amazonaws.com/dva-03-003/index.html

Functions for guest users were executed.

The current time is displayed.
Since you are not signed in at this time, this means that you are accessing this page as a guest user and the Lambda function for guest users was executed.

Verification: Accessing as signed-in user

The sign-up/sign-in process is then performed.
Please refer to the following page for details.

あわせて読みたい
Create sign-in page in Cognito user pool 【Configure the hosted UI of the Cognito user pool to create a sign-in page】 Create a sign-in page using Cognito. Cognito is an authentication system servic...

Once the sign-up/sign-in is completed, you will be redirected to this page again.

Functions for Sign-in users were executed.

The current date is now displayed.
This means that the sign-in process generated an ID token and executed the Lambda function for the signed-in user.

(Reference) ID pool

For reference, let’s check the Identity generated in the steps up to this point.

Two Identities have been created.
Two Identities have been created.

As you can see, two Identities have been created.
This means that not only a sign-in user, but also an Identity for the guest user was created, and a Lambda function for the guest was executed with it.

Summary

We have identified a way to change permissions for guest and sign-in users.
Specifically, the IAM role for the guest sign-in user is attached in the ID pool, and the process is branched depending on whether the user has an ID token or not.