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 users access to AWS resources.
Create a sign-in page using the hosted UI of the Cognito user pool.
Use JavaScript to perform authentication and authorization with Cognito.
We will use AWS SDK for JavaScript v3.
For this Oauth flow, select Implicit Grant.
Environment
Create an S3 bucket and enable the static website hosting feature. Prepare content by placing HTML files inside.
Combine the hosted UI of the Cognito user pool with the identity pool and authorize authenticated users to access SSM parameters.
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-fa/tree/main/012
Point Explanation
Hosted UI for Cognito User Pool
One of the features offered by the Cognito user pool is a hosted UI. This is a managed authentication feature. In this case, we will use it to build a sign-in page.
For more information on Cognito hosted UI, please see the following page.
This page will cover only the key points.
Resources:
UserPoolClient:
Type: AWS::Cognito::UserPoolClient
Properties:
AllowedOAuthFlowsUserPoolClient: true
AllowedOAuthFlows:
#- code
- implicit
AllowedOAuthScopes:
- openid
- profile
CallbackURLs:
- !Sub "${BucketWesSiteEndpointUrl}/${SigninHtml}"
ClientName: !Sub ${Prefix}-UserPoolClient
ExplicitAuthFlows:
- ALLOW_REFRESH_TOKEN_AUTH
- ALLOW_USER_SRP_AUTH
LogoutURLs:
- !Sub "${BucketWesSiteEndpointUrl}/${SignoutHtml}"
SupportedIdentityProviders:
- COGNITO
UserPoolId: !Ref UserPool
Code language: YAML (yaml)
The key setting is the AllowedOAuthFlows property.
Since this OAuth flow is Implicit Grant, set this property to “implicit”.
Next, check the URL of the sign-in/sign-out page by the hosted UI.
The URLs are as follows
- https://[prefix].auth.ap-northeast-1.amazoncognito.com/login?response_type=token&client_id=[app-client-id]&redirect_uri=[callback-url]
- https://[prefix].auth.ap-northeast-1.amazoncognito.com/logout?response_type=token&client_id=[app-client-id]&logout_uri=[sign-out-url]
The key point is the URL parameter response_type.
In case of Implicit Grant, set this parameter to “token” since the token can be obtained directly.
Granting Authorization to Authenticated Users in Cognito ID Pool
Let’s look at the resources related to the ID pool, which is responsible for authorization functions when accessing AWS resources.
First, let’s look at the ID pool itself.
Resources:
IdentityPool:
Type: AWS::Cognito::IdentityPool
Properties:
AllowUnauthenticatedIdentities: false
CognitoIdentityProviders:
- ClientId: !Ref UserPoolClient
ProviderName: !Sub "cognito-idp.${AWS::Region}.amazonaws.com/${UserPool}"
IdentityPoolName: !Sub ${Prefix}-IdentityPool
Code language: YAML (yaml)
The AllowUnauthenticatedIdentities property allows you to set whether to grant access privileges to AWS resources to users who are not authenticated in the user pool. In this case, only authenticated users will be granted access privileges, so this property is set to “false”.
In the CognitoIdentityProviders property, specify the user pool and app client to be associated with the identity pool.
Define the privileges to be granted to authenticated users as IAM roles and attach them to the identity pool.
Resources:
IdentityPoolRoleAttachment:
Type: AWS::Cognito::IdentityPoolRoleAttachment
Properties:
IdentityPoolId: !Ref IdentityPool
Roles:
authenticated: !GetAtt IdentityPoolAuthenticatedRole.Arn
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:
- ssm:GetParameter
Resource:
- !Sub "arn:aws:ssm:${AWS::Region}:${AWS::AccountId}:parameter/${AuthenticatedParameter}"
Code language: YAML (yaml)
This time, we will grant authenticated users access to the SSM parameter store.
Create an IAM role with permission to retrieve SSM parameter store strings and associate it with the identity pool.
For the federation settings, refer to the following page.
https://docs.aws.amazon.com/cognito/latest/developerguide/role-trust-and-permissions.html
Browser script to grant privileges to authenticated users
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 see the following page.
This page covers the key points.
npm package
First, identify the packages to be installed with npm.
The following command will install the three packages.
$ npm install @aws-sdk/client-cognito-identity
$ npm install @aws-sdk/credential-provider-cognito-identity
$ npm install @aws-sdk/client-ssm
Code language: Bash (bash)
This package is about Cognito and SSM.
Browser Script
First, check the description of package loading.
import {
CognitoIdentityClient
} from "@aws-sdk/client-cognito-identity";
import {
fromCognitoIdentityPool
} from "@aws-sdk/credential-provider-cognito-identity";
import {
SSMClient,
GetParameterCommand
} from "@aws-sdk/client-ssm";
Code language: JavaScript (javascript)
Load the required objects from the three packages you have just installed.
Next, we will review the parameter definitions.
const REGION = "[region]";
const USER_POOL_ID = "[cognito-user-pool-id]";
const IDENTITY_POOL_ID = "[cognito-id-pool-id]";
const PARAMETER_NAME = "[ssm-parameter-id]";
const params = new URLSearchParams(location.hash.slice(1));
const idToken = params.get("id_token");
Code language: JavaScript (javascript)
The first four lines are information such as region information and IDs of various resources.
The key point is the bottom two lines.
From the URL of the page after sign-in, we extract the string related to the ID token.
This is because this OAuth flow is Implicit Grant.
The URL of the page after sign-in is as follows.
https://s3-ap-northeast-1.amazonaws.com/fa-012/signin.html#id_token=[id-token]&expires_in=3600&token_type=Bearer
Create a client object to access SSM resources.
const ssmClient = new SSMClient({
region: REGION,
credentials: fromCognitoIdentityPool({
client: new CognitoIdentityClient({ region: REGION }),
identityPoolId: IDENTITY_POOL_ID,
logins: {
[`cognito-idp.${REGION}.amazonaws.com/${USER_POOL_ID}`]: idToken
}
}),
});
Code language: JavaScript (javascript)
To create an SSM client, obtain temporary credentials from the identity pool.
The name of the user pool, which is Idp, and the user pool’s ID token defined earlier are used for this purpose.
Define a function to retrieve parameters from the SSM parameter store.
const getParameter = async () => {
try {
const response = await ssmClient.send(
new GetParameterCommand({ Name: PARAMETER_NAME })
);
document.getElementById('parameter').innerText = `SSM Parameter Store: ${response.Parameter.Value}`;
} catch (err) {
console.log(err);
document.getElementById('parameter').innerText = 'Deny access to SSM Parameter Store.';
}
};
window.getParameter = getParameter;
Code language: JavaScript (javascript)
Pass a GetParameterCommand object to the SSM client you created to retrieve a string from the parameter store.
If successfully retrieved, the value is embedded in the HTML.
Define a function to retrieve the user name.
const getName = async () => {
try {
const tokens = idToken.split('.');
const tokenDecoded = JSON.parse(atob(tokens[1]));
document.getElementById('name').innerText = `Name: ${tokenDecoded.name}`;
} catch (err) {
console.log(err);
document.getElementById('name').innerText = 'Name: Guest';
}
};
window.getName = getName;
Code language: JavaScript (javascript)
The ID token for the user pool is a JWT token.
The ID token is a JSON web token (JWT) that contains claims about the identity of the authenticated user, such as name, email, and phone_number. You can use this identity information inside your application. The ID token can also be used to authenticate users to your resource servers or server applications.
Using the ID token
User information can be obtained by decoding.
In this case, the user name information obtained from the token is embedded in the p tag.
For more information on the payload of ID tokens, please refer to the following page.
By building this file with npm, the browser script is complete.
HTML for page after sign-in
<html>
<head>
</head>
<body>
<h1>signin.html</h1>
<p id="name"></p>
<p id="parameter"></p>
<script type="text/javascript" src="./main.js"></script>
<script>
getName();
getParameter();
</script>
</body>
</html>
Code language: HTML, XML (xml)
Simple is an HTML file.
It executes the two functions we just reviewed and dynamically displays the user name and a string retrieved from the SSM parameter store.
S3 Static Website Hosting
The static website hosting function publishes HTML files placed in S3 buckets.
Please see the following page for details.
SSM Parameter Store
Resources:
AuthenticatedParameter:
Type: AWS::SSM::Parameter
Properties:
Name: !Sub "${Prefix}-authenticated"
Type: String
Value: Authenticated
Code language: YAML (yaml)
No special configuration is required; just store the string in the parameter store.
In this case, configure as follows
- Parameter name: fa-012-authenticated
- Value to be stored: Authenticated
Architecting
We will use CloudFormation to build this environment and check its actual behavior.
Create CloudFormation stacks and check resources in stacks
Create a CloudFormation stacks.
For more information on how to create stacks and check each stack, please refer to the following page.
After checking the resources for each stack, the information for the main resource created this time is as follows
- Name of the S3 bucket: fa-012
- Cognito user pool ID: ap-northeast-1_W8ZPnQYRK
- Cognito user pool app client ID: 2lp4695vpuo88ettvk78lmq694
- Cognito user pool domain prefix: fa-012
- Cognito ID pool ID: ap-northeast-1:6d117ebe-506b-4f0d-b7dc-a438a4dbd57b
From the above, the URL for the sign-in/sign-out page with the hosted UI mentioned above is defined as follows
- URL for sign-in page
- https://[prefix].auth.ap-northeast-1.amazoncognito.com/login?response_type=token&client_id=[app-client-id]&redirect_uri=[callback-url]
- URL for sign-out page
- https://[prefix].auth.ap-northeast-1.amazoncognito.com/logout?response_type=token&client_id=[app-client-id]&logout_uri=[sign-out-url]
Place HTMLs in S3 bucket
Place the HTML in the S3 bucket and prepare the contents after sign-in/sign-out.
Put the sign-in/sign-out URL described above in index.html, and then install it using the AWS CLI.
$ aws s3 cp html s3://fa-012/ --recursive
$ aws s3 cp main.js s3://fa-012
Code language: Bash (bash)
Accessing content for authenticated users: on success
Now that we are ready, let’s actually access the hosted UI.
First, access the following URL
https://s3-ap-northeast-1.amazonaws.com/fa-012/index.html
The root page, index.html, will be displayed.
Click “Sign In” to go to the sign-in page.
The sign-up process is required only for the first time. For more information about the signup process, please refer to Creating sign in page in Cognito user pool – Accessing the hosted UI: Sign up.
In this case, we will register the user name “awstut”. The sign-in page is as follows
After entering your email address and password, click “Sign In”.
The signin.html page is now displayed. The user’s name and the SSM parameter store values are displayed. The former was retrieved from the identity token, while the latter was retrieved from the identity pool using temporary credentials to access the parameter store.
From the above, we were able to publish content restricted to authenticated users by combining Cognito user pool and identity pool.
Accessing content for authenticated users: In case of error
Finally, let’s check the behavior when the sign-in process fails or is not performed.
In the previous example, we went through the sign-in URL to access signin.html, but now we will access signin.html directly.
The signin.html is now displayed, but the content is different from what we saw earlier. Because we accessed the page directly, there was no ID token information and the credential acquisition failed. Therefore, the user name became “Guest” and the SSM parameter store could not be accessed.
From the above, we have confirmed that only an authenticated user can access AWS resources.
Summary
By combining the Cognito user pool, hosted UI and identity pool, we were able to confirm that we can grant access privileges to AWS resources only to authenticated users.