DVA

Cognito IDプールでゲスト/サインインユーザーで権限を分ける

Cognito IDプールでゲスト/サインインユーザーで権限を分ける

Cognito IDプールは認証済みユーザーに加えて、未認証のゲストユーザーに対しても、一時的なクリデンシャルを生成することができます。
今回はゲスト・サインインユーザーに対して、異なる権限を与える構成を確認します。

構築する環境

AWS認定アソシエイト3資格対策~ソリューションアーキテクト、デベロッパー、SysOpsアドミニストレーター~ | 平山毅, 堀内康弘, 福垣内孝造, 岡智也, 池田大, 原江梨佳, 澤田拓也, 原俊太郎, 仲村勇亮, 上村祐輝, 鳥谷部昭寛 | 工学 | Kindleストア | Amazon
Amazonで平山毅, 堀内康弘, 福垣内孝造, 岡智也, 池田大, 原江梨佳, 澤田拓也, 原俊太郎, 仲村勇亮, 上村祐輝, 鳥谷部昭寛のAWS認定アソシエイト3資格対策~ソリューションアーキテクト、デベロッパー、SysOpsアドミニストレーター~。アマゾンならポイント還元本が多数。一度購入いただいた電子書籍は、K...
Diagram of change permissions for guest/sign-in users in Cognito ID Pool.

S3バケットを作成し、静的ウェブサイトホスティング機能を有効にします。
内部にHTMLファイルを設置して、コンテンツを用意します。

CognitoユーザープールのホストされたUIでサインイン機能を作成します。

Cognito IDプールでゲスト・サインインユーザーに権限を割り当てるように設定します。
具体的には、2つのLambda関数を作成し、それぞれ対応するユーザーが実行できるように設定します。

CloudFormationテンプレートファイル

上記の構成をCloudFormationで構築します。
以下のURLに、CloudFormationテンプレートに加え、ブラウザスクリプト等を配置してます。

awstut-dva/03/003 at main · awstut-an-r/awstut-dva
Contribute to awstut-an-r/awstut-dva development by creating an account on GitHub.

テンプレートファイルのポイント解説

サインインユーザーとゲストユーザーで権限を分けるため、Cognito IDプールを設定を中心に確認します。

IDプールにゲスト・サインインユーザー用のIAMロールをアタッチする

Cognito IDプールでユーザーに権限を与えるためには、対応するIAMロールをアタッチするという方法になります。

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)

IDプール本体は特別な設定を行いません。
ユーザープールと連携するように設定するだけです。

ポイントはIAMロールをアタッチメントです。
今回はサインイン・ゲストユーザーに別々の権限を割り当てますので、それぞれauthenticated・unathenticatedプロパティを使用して、IAMロールを設定します。

今回は以下のIAMロールを定義します。

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)

ConditionプロパティでIAMロールを引き受けるための条件を設定します。
詳細は以下のページをご確認ください。

ロールの信頼とアクセス権限 - Amazon Cognito
Amazon Cognito の信頼ポリシーの例。

Policiesプロパティで、付与する権限を定義します。
Lambda関数を指定して実行を許可します。

実行するLambda関数

Cognitoとは関係ありませんが、Lambda関数の定義を確認します。

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)

インラインで実行するコードを記載します。
Lambda関数の定義方法の詳細については、以下のページをご確認ください。

ランタイム環境はPython3.9を選択し、それぞれ以下を実行するように設定します。

  • サインインユーザー用関数:本日の日付を返す。
  • ゲストユーザー用関数:現在の時刻を返す。

ブラウザスクリプト

JavaScriptを使用して、サインイン後のCognito機能を実装します。
AWS SDK for JavaScript v3を使用してブラウザスクリプトを作成する手順は、以下のページをご確認ください。

本ページでは、ゲスト・サインインユーザーの振り分けに関する内容を中心に取り上げます。

IDトークン取得

今回の構成では、OAuthフローにImplicit Grant(暗黙の付与)を設定しています。
Implicit Grantですと、URLパラメータからIDトークンを取得することができます。
サインインユーザーの場合、以下のコードでIDトークンを取得することができます。

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

詳細は以下のページをご確認ください。

ユーザーに応じてLambdaクライアント作成

Lambda関数にアクセスするために、クライアントオブジェクトを作成します。
ポイントはIDトークンの有無によって、対応を分ける点です。

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)

サインインユーザーの場合、先述のコードによって、IDトークンの取得できます。
ですからそのトークンを使って、クライアントオブジェクトを作成します。

一方ゲストユーザーの場合、サインインしていませんから、IDトークンを取得できません。
ですからIDトークンを使わない形で、クライアントオブジェクトを作成します。

このクライアントオブジェクト時に、IDプールにアタッチしたIAMロールに基づいたクリデンシャルが使われます。
つまりユーザーに応じた権限が設定されているということです。

Lambda関数実行

この後の処理は共通です。
クライアントオブジェクトを使ってLambda関数を実行します。

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

Lambda関数を実行し、実行結果をHTMLに埋め込みます。
先述の通り、クリデンシャルで付与された権限の範囲内で関数を実行します。

HTMLファイル

参考にHTMLも確認します。

<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)

クラス名がfunction-resultのpタグ内に、Lambda関数の実行結果を埋め込みます。

サインイン・サインアウト用のURLを設置します。
リダイレクトURLはこのindex.htmlを指すように設定します。

環境構築

CloudFormationを使用して、本環境を構築し、実際の挙動を確認します。

CloudFormationスタックを作成し、スタック内のリソースを確認する

CloudFormationスタックを作成します。
スタックの作成および各スタックの確認方法については、以下のページをご確認ください。

S3バケットにコンテンツを設置する

S3バケットにHTML・JavaScriptファイルを設置します。

Two files are uploaded to the S3 bucket.

この2ファイルがサインイン/サインアウト後のコンテンツです。

検証:ゲストユーザーでアクセス

準備が整いましたので、実際にコンテンツにアクセスします。
以下のURLにアクセスします。

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

Functions for guest users were executed.

現在の時刻が表示されました。
これは現時点ではサインインしていませんので、ゲストユーザーとして本ページにアクセスしていることになり、ゲストユーザー用のLamabda関数が実行されたということです。

検証:サインインユーザーでアクセス

続いてサインアップ/サインイン処理を行います。
詳細は以下のページをご確認ください。

サインアップ/サインインが完了しましたら、改めて本ページにリダイレクトされます。

Functions for Sign-in users were executed.

現在の日付が表示されました。
これはサインイン処理によって、IDトークンが生成され、サインインユーザー用のLambda関数が実行されたということです。

(参考)IDプール

参考までに、ここまでの手順で生成されたIdentityを確認します。

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

ご覧の通り、2つのIdentityが作成されていることがわかります。
つまりサインインユーザーだけでなく、ゲストユーザー用のIdentityも生成され、それを使ってゲスト用のLambda関数が実行されたということです。

まとめ

ゲスト・サインインユーザーで権限を分ける方法を確認しました。
具体的には、IDプールでゲスト・サインインユーザー用のIAMロールをアタッチした上で、IDトークンの有無によって処理を分岐させる方法です。

タイトルとURLをコピーしました