AWS

Cognitoユーザープールの認証後にIDプールで権限を与える(Implicit Grant編)

スポンサーリンク
Cognitoユーザープールの認証後にIDプールで権限を付与する - Implicit Grant編 AWS
スポンサーリンク
スポンサーリンク

Cognitoユーザープールの認証後にIDプールで権限を与える

CognitoユーザープールとIDプールを使用して、サインインユーザーにAWSリソースにアクセスするための権限を付与します。

CognitoユーザープールのホストされたUIを使用して、サインインページを作成します。

Cognitoによる認証・認可を実行するためにJavaScriptを使用します。
今回はAWS SDK for JavaScript v3を使用します。

今回のOauthフローでは、Implicit Grant(暗黙の付与)を選択します。

構築する環境

Amazon.co.jp: AWS認定資格試験テキスト AWS認定ソリューションアーキテクト - アソシエイト 改訂第2版 : NRIネットコム株式会社, 佐々木 拓郎, 林 晋一郎, 金澤 圭: 本
Amazon.co.jp: AWS認定資格試験テキスト AWS認定ソリューションアーキテクト - アソシエイト 改訂第2版 : NRIネットコム株式会社, 佐々木 拓郎, 林 晋一郎, 金澤 圭: 本
Diagram of Authorization by Cognito ID Pool after Authentication by User Pool - Implicit Grant Ver

S3バケットを作成し、静的ウェブサイトホスティング機能を有効にします。内部にHTMLファイルを設置して、コンテンツを用意します。
CognitoユーザープールのホストされたUIとIDプールを組み合わせ、認証ユーザーに対して、SSMパラメータにアクセスする権限を与えます。

環境構築用のCloudFormationテンプレートファイル

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

awstut-fa/012 at main · awstut-an-r/awstut-fa
Contribute to awstut-an-r/awstut-fa development by creating an account on GitHub.

ポイント解説

CognitoユーザープールのホストされたUI

Cognitoユーザープールが提供する機能の1つに、ホストされたUIというものがあります。マネージドの認証機能です。今回はこれを使用してサインインページを構築します。
Cognito ホストされたUIについては、以下のページをご確認ください。

本ページでは、ポイントとなる点のみを取り上げます。

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)

ポイントとなる設定はAllowedOAuthFlowsプロパティです。
今回のOAuthフローはImplicit Grant(暗黙の付与)ですので、このプロパティに「implicit」を設定します。

次にホストされたUIによるサインイン/サインアウトページのURLを確認します。
それぞれ以下のURLとなります。

  • サインインページ用URL:https://[プレフィックス].auth.ap-northeast-1.amazoncognito.com/login?response_type=token&client_id=[アプリクライアントID]&redirect_uri=[サインイン後のリダイレクト先URL]
  • サインアウトページ用URL:”https://[プレフィックス].auth.ap-northeast-1.amazoncognito.com/logout?response_type=token&client_id=[アプリクライアントID]&logout_uri=[サインアウト後のリダイレクト先URL]

ポイントはURLパラメータのresponse_typeです。
Implicit Grantの場合、直接トークンを取得できますので、本パラメータを「token」に設定します。

Cognito IDプールで認証ユーザーに権限を付与する

IDプール関係のリソースを確認します。IDプールはAWSリソースにアクセスする際の認可機能を司ります。
まずIDプール本体です。

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)

AllowUnauthenticatedIdentitiesプロパティで、ユーザープールにて認証されていないユーザーに対しても、AWSリソースへのアクセス権限を付与するかどうかを設定できます。今回は認証されたユーザーに対してのみアクセス権を認めますので、本プロパティは「false」とします。
CognitoIdentityProvidersプロパティで、IDプールに関連づけるユーザープールおよびアプリクライアントを指定します。

認証ユーザーに付与する権限をIAMロールととして定義し、IDプールにアタッチします。

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)

今回は認証ユーザーに対して、SSMパラメータストアに対するアクセス権限を許可します。
SSMパラメータストアの文字列を取得できる権限を持ったIAMロールを作成し、IDプールに関連づけます。
なおフェデレーションに関する設定は、「ロールの信頼とアクセス権限」を参考に設定しました。

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

認証ユーザに権限を付与するブラウザスクリプト

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

本ページでは、ポイントとなる箇所を取り上げます。

npmパッケージ

まずnpmでインストールするパッケージを確認します。
以下のコマンドで、3つのパッケージをインストールします。

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

CognitoとSSMに関するパッケージです。

ブラウザスクリプト

まずパッケージの読み込みに関する記述を確認します。

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)

先ほどインストールした3パッケージから、必要なオブジェクトを読み込みます。

次にパラメータの定義に関して確認します。

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)

最初の4行は、リージョン情報や、各種リソースのIDといった情報です。

ポイントは下2行です。
サインイン後のページのURLから、IDトークンに関する文字列を抽出しています。
これは今回のOAuthフローがImplicit Grant(暗黙の付与)であるためです。
なおサインイン後のページのURLは、以下のようなものです。

https://s3-ap-northeast-1.amazonaws.com/fa-012/signin.html#id_token=[id-token]&expires_in=3600&token_type=Bearer

SSMリソースにアクセスするためのクライアントオブジェクトを作成します。

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)

SSMクライアントを作成するために、IDプールから一時的なクリデンシャルを取得します。
その際に、Idpであるユーザープールの名前と、先ほど定義したユーザープールのIDトークンを使用します。

SSMパラメータストアからパラメータを取得する関数を定義します。

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)

作成したSSMクライアントに、GetParameterCommandオブジェクトを渡して、パラメータストアから文字列を取得します。
正常に取得できた場合は、その値をHTMLに埋め込みます。

ユーザー名を取得する関数を定義します。

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)

ユーザープールのIDトークンはJWTトークンです。

ID トークンとは、name、email、および phone_number などの認証されたユーザーのアイデンティティに関するクレームが含まれる JSON Web トークン (JWT) です。この ID 情報はアプリケーション内で使用できます。ID トークンは、リソースサーバーまたはサーバーアプリケーションに対するユーザーの認証にも使用できます。

ID トークンの使用

デコードすることでユーザー情報を取得できます。今回はトークンから取得したユーザーの名前(name)情報を、pタグ内に埋め込みます。IDトークンのペイロードに関しては、ID トークンの使用をご確認ください。

このファイルをnpmでビルドすることで、ブラウザスクリプトは完成です。

サインイン後のページ用ファイル

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

シンプルはHTMLファイルです。
先ほど確認した2つの関数を実行し、動的にユーザー名とSSMパラメータストアから取得した文字列を表示させます。

SSMパラメータストア

Resources: AuthenticatedParameter: Type: AWS::SSM::Parameter Properties: Name: !Sub "${Prefix}-authenticated" Type: String Value: Authenticated
Code language: YAML (yaml)

特別な設定は行わず、パラメータストアに文字列を保存します。
今回は以下の通りに設定します。

  • パラメータ名:fa-012-authenticated
  • 保存する値:Authenticated

環境構築

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

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

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

各スタックのリソースを確認した結果、今回作成された主要リソースの情報は以下の通りです。

  • S3バケットの名前:fa-012
  • CognitoユーザープールのID:ap-northeast-1_W8ZPnQYRK
  • Cognitoユーザープール アプリクライアントのID:2lp4695vpuo88ettvk78lmq694
  • Cognitoユーザープール ドメインプレフィックス:fa-012
  • Cognito IDプールのID:ap-northeast-1:6d117ebe-506b-4f0d-b7dc-a438a4dbd57b

以上より、先述のホストされるUIによるサインイン/サインアウトページ用のURLは以下に定まります。

  • サインインページ用URL
    • https://fa-012.auth.ap-northeast-1.amazoncognito.com/login?response_type=token&client_id=2lp4695vpuo88ettvk78lmq694&redirect_uri=https://s3-ap-northeast-1.amazonaws.com/fa-012/signin.html
  • サインアウトページ用URL
    • https://fa-012.auth.ap-northeast-1.amazoncognito.com/logout?response_type=token&client_id=2lp4695vpuo88ettvk78lmq694&logout_uri=https://s3-ap-northeast-1.amazonaws.com/fa-012/signout.html

S3バケットにHTMLを設置する

S3バケットにHTMLを設置し、サインイン/サインアウト後のコンテンツを用意します。

先述のサインイン/サインアウト用URLをindex.htmlに記載後、AWS CLIを使用して設置します。

$ aws s3 cp ./html s3://fa-012/ --recursive $ aws s3 cp main.js s3://fa-012
Code language: Bash (bash)

認証ユーザー向けコンテンツにアクセスする:成功時

準備が整いましたので、実際にホストされたUIにアクセスします。

まず以下のURLにアクセスします。

https://s3-ap-northeast-1.amazonaws.com/fa-012/index.html

するとルートページであるindex.htmlが表示されます。

Access the Cognito user pool sign-in page.

「Sign In」を押下すると、サインインページが表示されます。

初回に限り、サインアップ処理を必要です。サインアップ処理に関しては、「Cognito ユーザープールでログインページを作成する – ホストされたUIにアクセスする:サインアップ」をご確認ください。

今回は「awstut」というユーザー名を登録します。サインインページは以下の通りです。

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

メールアドレスおよびパスワードを入力後、「Sign In」を押下します。

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

signin.htmlページが表示されました。ユーザーの名前およびSSMパラメータストアの値が表示されています。前者はIDトークンから取得し、後者はIDプールから一時的なクリデンシャルを使用して、パラメータストアにアクセスして取得できました。

以上より、CognitoユーザープールおよびIDプールを組み合わせることで、認証ユーザーに限定したコンテンツを公開することができました。

認証ユーザー向けコンテンツにアクセスする:エラー時

最後にサインイン処理に失敗、または未実施の場合の挙動を確認します。

先ほどはサインイン用URLを経てsignin.htmlにアクセスしましたが、今度は直接signin.htmlにアクセスします。

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

signin.htmlが表示されましたが、先ほどは内容が異なります。直接ページにアクセスしたことで、IDトークン情報がなく、クリデンシャル取得に失敗しました。よってユーザー名が「Guest」となり、SSMパラメータストアへもアクセスできませんでした。

以上より、認証ユーザーでなければAWSリソースにアクセスできないことを確認しました。

まとめ

Cognitoユーザープール、ホストされたUIおよびIDプールを組み合わせることで、認証ユーザーのみに限定してAWSリソースにアクセス権限を与えることができることを確認できました。

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