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
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
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.
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.
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
Place content in S3 buckets
Place HTML and JavaScript files in 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
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.
Once the sign-up/sign-in is completed, you will be redirected to this page again.
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.
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.