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

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

Diagram of Authorization by Cognito ID Pool after Authentication by User Pool - Implicit Grant Ver

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.

あわせて読みたい
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...

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

  • 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]
  • 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.

    あわせて読みたい
    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 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.

    https://docs.aws.amazon.com/cognito/latest/developerguide/amazon-cognito-user-pools-using-the-id-token.html

    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.

    あわせて読みたい
    Publish your site with S3 static website hosting 【Configure the S3 static website hosting to publish your site】 Find out how to use the S3 static website hosting to publish a website. If your website cons...

    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.

    あわせて読みたい
    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 ...

    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.

    Access the Cognito user pool sign-in page.

    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

    Enter your username and password to sign-in to the Cognito user pool.

    After entering your email address and password, click “Sign In”.

    If you have successfully signed in, you can access AWS resources.

    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.

    If the sign-in fails, you will be treated as a guest user.

    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.