【AWS】AWS Cognito IDプール（フェデレーティッド ID）を用いてAWSの一時「Credentials Key」を発行する方法

## 背景
- [AWS Cognito](https://aws.amazon.com/jp/cognito/)はユーザーの認証と認可を処理できるサービスです。
- [AWS Cognito](https://aws.amazon.com/jp/cognito/)を使用すれば、ユーザーのデータベースを[AWS RDS](https://aws.amazon.com/jp/rds/)などのAWSの別サービスで作成することなく、ユーザーのサインアップやサインイン機能を実装することができます。
- アカウントのデータ管理などがAWS側になるフルマネージドサービスなので、セキュリティ的にも自社でユーザーのデータベースを持つより安心かと思います。
- [AWS Cognito](https://aws.amazon.com/jp/cognito/)には[Amazon Cognito ID プール (フェデレーティッド ID)](https://docs.aws.amazon.com/ja_jp/cognito/latest/developerguide/cognito-identity.html)という機能があります。
- この機能を利用すれば、AWSの一時「Credentials Key」を発行することができので、一定時間だけ使用できるIAMユーザー（ロール）のようなものをこの機能で実現することができます。
- この機能を使用すれば、例えば、s3へデータをupするWEBアプリの実装などが簡単にできます。

## 目標
- [AWS Cognito](https://aws.amazon.com/jp/cognito/)の[AWS Cognito ID プール (フェデレーティッド ID)](https://docs.aws.amazon.com/ja_jp/cognito/latest/developerguide/cognito-identity.html)を使用し、AWSの一時「Credentials Key」を発行し、IAMを切り替える。

## [ユーザープール](https://docs.aws.amazon.com/ja_jp/cognito/latest/developerguide/cognito-user-identity-pools.html)と[IDプール（フェデレーティッド ID）](https://docs.aws.amazon.com/ja_jp/cognito/latest/developerguide/cognito-identity.html)について
- 私はAWS Cognitoを始めて触った際に、どのようなサービスが全くわからなかったため、上記の2リソースの違いを理解するのに苦労しました。
- 以下の2つの資料が2つのリソースの違いを理解するのに非常に参考になりました。
    - [AWS CLIで動かして学ぶCognito IDプールを利用したAWSの一時クレデンシャルキー発行](https://dev.classmethod.jp/articles/get-aws-temporary-security-credentials-with-cognito-id-pool-by-aws-cli/)
    - [AWSを活用したユーザー認証実装パターン解説 | AWS Summit Tokyo 2019](https://www.youtube.com/watch?v=wnB6Fq7-tHs)
    (動画の22:47あたりで表示されている構成図が非常にわかりやすかったですので、一度ご覧になることをお勧めします。)
- [AWS Cognito](https://aws.amazon.com/jp/cognito/)のコンソール画面に移動すると以下のように
    1. [ユーザープール](https://docs.aws.amazon.com/ja_jp/cognito/latest/developerguide/cognito-user-identity-pools.html)
    2. [IDプール（フェデレーティッド ID）](https://docs.aws.amazon.com/ja_jp/cognito/latest/developerguide/cognito-identity.html)
    
    の2つのリソースがサイドバーに表示されます。
![01_0048.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2651220/c70c88ac-b4b8-ed25-b393-907c4ea86e01.png)

- [IDプール（フェデレーティッド ID）](https://docs.aws.amazon.com/ja_jp/cognito/latest/developerguide/cognito-identity.html)を使用する上で、[ユーザープール](https://docs.aws.amazon.com/ja_jp/cognito/latest/developerguide/cognito-user-identity-pools.html)の理解はかかせないのでまずはこの2つのリソースの機能について説明します。

| リソース名                        | 機能       | 個人的なイメージ                           | 
| --------------------------------- | ---------- | ------------------------------------------ | 
| ユーザープール                    | 認証を行う | ユーザー情報を保存するデータベース         | 
| IDプール（フェデレーティッド ID） | 認可を行う | AWSのIAM関連の許認可を与えてくれるリソース | 

- それぞれのリソースの機能と個人的なイメージを上記の通り表にまとめました。
- ユーザープールはユーザーの認証を行うリソースで、ユーザーのサインインやログアウトに使用されるリソースです。
- そして、IDプール（フェデレーティッド ID）は「AWS Credentials」を発行する認可機能を担っています。
- IDプール（フェデレーティッド ID）は外部プロバイダ（GoogleやAppleなどのログイン機能と連携できるらしい）との接続も可能の模様です。
- IDプール（フェデレーティッド ID）をユーザープールと連携させて使用する場合の流れは以下のようなイメージです。
    1. ユーザープールで登録されたユーザーにログインする。
    2. ログインした際に取得できる`token`をIDプール（フェデレーティッド ID）に送る。
    3. IDプール（フェデレーティッド ID）が一時「AWS Credentials Key」を発行する。
    4. 発行された「AWS Credentials Key」を使用してAWSリソースにアクセスして各種操作を行う。
- 上記の通り、IDプール（フェデレーティッド ID）を使用して「AWS Credentials Key」を発行するには何かしらのユーザーの認証機能が必要となります。
- この記事では、ユーザープールを認証機能として使用し、「AWS Credentials Key」を発行する方法を説明します。

## ユーザープールの作成方法
- まずは、IDプール（フェデレーティッド ID）と連携させるユーザープールをAWSのコンソールで作成します。
### 「ユーザープール」に移動します。
![02_0048.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2651220/27761f91-ef6a-d895-0b1c-81f9b30296fd.png)

### 「ユーザープールを作成」をクリックします。
![03_0048.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2651220/ff6196f8-15b9-568a-186b-002cb67e090b.png)

### 「サインインエクスペリエンス」の設定を行います。
- 今回は「Eメール」でのサインインを行うので、「Eメール」をチェックし、「次へ」をクリックします。
![04_0048.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2651220/81d0dded-43bf-a8cf-8127-053515b25066.png)

### 「セキュリティ要件」の設定を行います。
- 今回は「MFAなし」でテストを行うので「MFAなし」を選択し、「次へ」をクリックします。
- セキュリティを強化したい場合などは適宜「パスワードポリシー」などを変更して下さい。
![05_0048.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2651220/a2590dc5-5f74-5016-fb6c-e8aa2ead0eec.png)

### 「サインアップエクスペリエンス」の設定を行います。
- 「サインアップエクスペリエンス」はデフォルトのままの設定で良いので、そのまま「次へ」をクリックします。
![06_0048.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2651220/8f2f022f-0276-c227-844f-f58a5b80fd6d.png)

### 「メッセージ配信」の設定を行います。
- 「Eメールプロバイダー」を「CognitoでEメールを送信」に変更し、「次へ」をクリックします。
（おそらく「Amazon SES」を使用する場合は、SES側で何か設定が必要そうだからです。）
![07_0048.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2651220/99d54eab-b63e-27ed-ffef-5b31ec5ef1db.png)

### 「アプリケーションを統合」の設定を行います。
1. 「ユーザープール名」を入力します。
    ここでは、「dev_user_pool_01」とします。
2. 「アプリケーションクライアント名」を入力します。
    ここでは、「dev_user_app_client_01」とします。
3. 「高度なアプリケーションクライアントの設定」の折りたたみを開きます。
4. 「認証フロー」のプルダウンから「ALLOW_ADMIN_USER_PASSWORD_AUTH」を選択し「認証フロー」として追加します。
    なお、「ALLOW_ADMIN_USER_PASSWORD_AUTH」の旧名は「ADMIN_NO_SRP_AUTH」です。
    > 【引用：[ユーザープール認証フロー](https://docs.aws.amazon.com/ja_jp/cognito/latest/developerguide/amazon-cognito-user-pools-authentication-flow.html)】
    ・CreateUserPoolClient または UpdateUserPoolClient を呼び出すときは、ExplicitAuthFlow パラメータに ALLOW_ADMIN_USER_PASSWORD_AUTH (以前は ADMIN_NO_SRP_AUTH と呼ばれています) を含めます。
5. 「次へ」をクリックします。
![08_0048.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2651220/1cd6711c-a608-3f09-e672-4840b050a1cd.png)

### 全ての設定を確認し、ユーザープールを作成します。
- 最後に、全ての設定に間違いがないか確認する画面が出ます。
- 問題なければ「ユーザープールを作成」をクリックし、ユーザープールを作成します。
![09_0048.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2651220/60d59012-bd57-30c6-469f-8b45f618ea21.png)

### ユーザープールが作成されたことを確認します。
- ユーザープールが正常に作成されるとユーザープールのトップ画面に作成したユーザープール名が表示されます。
![10_0048.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2651220/2ba507b4-3ea8-d2f7-22e8-6124921ef87d.png)

## IDプール（フェデレーティッド ID）の作成方法
- ユーザープールが作成できたので、次にIDプール（フェデレーティッド ID）を作成していきます。
- IDプール（フェデレーティッド ID）の作成には、ユーザープールの「ユーザープールID」と「アプリクラインアントID」が必要になるので、事前にそれを確認して控えておきます。
- IDプール（フェデレーティッド ID）はクリックするとUIが古いAWSコンソールの画面になります。
- おそらくは、まだデザインが対応していないのかと思われますが、非常に使用しずらい（フェデレーティッド IDに（IDプール）とついていないのも記事を読んだ際に混乱するし、、、）UIなので、早く新しいデザインに対応して欲しいところです。。。

### 「フェデレーティッドID」の作成に必要な「ユーザープールID」を控えます。
- 「ユーザープールID」を控えたら、「ユーザープール名」をクリックしてユーザプールの詳細画面に移動します。
![11_0048.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2651220/a70f5aea-2fc2-de72-75b6-8374f83956c3.png)

### 「フェデレーティッドID」の作成に必要な「アプリクラインアントID」を控えます。
- 以下の手順で、「アプリクラインアントID」が記載されている画面に遷移し、「アプリクラインアントID」を控えます。
![12_0048.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2651220/5db13ea0-8a6d-4844-ca2e-25526310944e.png)
![13_0048.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2651220/7cbc99ac-0847-31f5-62d7-c3e4cc16f314.png)

### 「フェデレーティッドID」に移動します。
![14_0048.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2651220/1738b1c8-c9e1-30e7-2b30-fd4f06b01bcc.png)

### 「フェデレーティッドID」を作成します。
1. 「IDプール名」を入力します。
    ここでは、「dev_id_pool_01」とします。
2.  先ほど控えておいた「ユーザープールID」と「アプリクライアントID」を入力します。
3. 「プールの作成」をクリックし、IDプール（フェデレーティッド ID）を作成します。
![15_0048.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2651220/442684af-ae25-cea5-4215-875dffe9f7ee.png)

### 「フェデレーティッドID」に付与するIAMロールを作成しアタッチします。
- ユーザープールで認証され、IDプール（フェデレーティッド ID）で認可されたユーザーに付与するIAMロールを作成し、アタッチします。
- 認証されていない場合にもIAMロールを渡すことができるので、ここでは
    1. 認証されていない時に使用されるロール
    2. 認証された時に使用されるロール

    の2つのIAMロールが作成されています。
![16_0048.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2651220/f9a6d4b1-ed52-74aa-6aa7-39219d436431.png)

## ユーザープールにユーザーをAWS CLIで登録する方法
- コンソールでユーザープールとIDプール（フェデレーティッド ID）の作成が完了しました。
- 次に、ログインに使用するアカウントをユーザプールに新規登録をAWS CLIで行います。
- 作業をしているAWSアカウントにCLIでアクセスできるターミナル等を開き、以下の手順でアカウントをユーザープールに登録します。
- コマンドでは、
    1. アプリケーションクライアントID
    2. 登録するEメール（受信可能なものにすること）
    3. ログインに使用するパスワード

    の3つが必要になります。
```bash:user_poolにアカウントを登録するコマンド$
$ export APP_CLIENT_ID="xxxxxxxxxxxxxxxxxxxxxxxxxx"
$ export USER_EMAIL="email@example.com"
$ export PASSWORD="ExPassWord01@"
$ aws cognito-idp sign-up \
  --client-id ${APP_CLIENT_ID} \
  --username ${USER_EMAIL} \
  --password ${PASSWORD} \
  --user-attribute "Name=email,Value=${USER_EMAIL}" --region ap-northeast-1
```
- AWS CLIコマンドがうまく通れば、以下のような出力が表示されます。
```bash:出力
{
    "UserConfirmed": false,
    "CodeDeliveryDetails": {
        "Destination": "t***@o***",
        "DeliveryMedium": "EMAIL",
        "AttributeName": "email"
    },
    "UserSub": "0996399d-1d78-48d2-b426-*********"
}
```
- 登録したメールに認証用の6桁数字が届きますので、その数字を「CONFIRMATION_CODE」として入力しCLIで認証を行います。
```bash:user_poolのEメール認証を行うコマンド
$export CONFIRMATION_CODE=000000
$aws cognito-idp confirm-sign-up \
  --client-id ${APP_CLIENT_ID} \
  --username ${USER_EMAIL} \
  --confirmation-code ${CONFIRMATION_CODE} --region ap-northeast-1
```

### 登録がうまくできているかユーザープールを確認します。
- 登録したEメールと「確認ステータス」が「確認済み」になっていれば正常に登録ができています。
![17_0048.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2651220/6b511e57-184f-7c90-4a51-894646317138.png)

## 登録したユーザーでログインをし、IDプール（フェデレーティッド ID）から「AWS Credentials Key」を発行する方法
- 最後に登録したユーザーでログインし、「AWS Credentials Key」を発行し、IAMが切り替わっていることを確認します。
- まず、現在のIAMのステータスを以下のコマンドで確認します。
```bash:
$aws sts get-caller-identity
```
- 使用しているIAMに応じて、IAMのステータスが表示されます。
```bash:出力
{
    "UserId": "AROAS***********:admin-sso",
    "Account": "1393******",
    "Arn": "arn:aws:sts::13931******:assumed-role/AWSReservedSSO_AdministratorAccess_22f********/admin-sso"
}
```
- 以下のコマンドでIAMの切り替えに必要な情報を取得します。
- なお、コマンド中の「IDENTITY_POOL_ID」は以下のコンソール画面で確認できます。
![18_0048.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2651220/8c6eb3aa-0312-20c4-7241-e4aee72fd833.png)

```bash:
$export REGION="ap-northeast-1"
$export USER_POOL_ID="ap-northeast-1_xxxxx"
$export APP_CLIENT_ID="xxxxxxxxxxxxxxxxxxxxxxxxxx"
$export USER_EMAIL="email@example.com"
$export PASSWORD="ExPassWord01@"
$export IDENTITY_POOL_ID="ap-northeast-1:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
$export COGNITO_USER_POOL="cognito-idp.${REGION}.amazonaws.com/${USER_POOL_ID}"

$ ID_TOKEN=$(aws cognito-idp admin-initiate-auth \
  --user-pool-id ${USER_POOL_ID} \
  --client-id ${APP_CLIENT_ID} \
  --auth-flow ADMIN_NO_SRP_AUTH \
  --auth-parameters "USERNAME=${USER_EMAIL},PASSWORD=${PASSWORD}" \
  --query "AuthenticationResult.IdToken"  --region ap-northeast-1\
  --output text) && echo ${ID_TOKEN}

$ IDENTITY_ID=$(aws cognito-identity get-id \
  --identity-pool-id ${IDENTITY_POOL_ID} \
  --logins "${COGNITO_USER_POOL}=${ID_TOKEN}" \
  --query "IdentityId"  --region ap-northeast-1\
  --output text) && echo ${IDENTITY_ID}

$ OUTPUT=$(aws cognito-identity get-credentials-for-identity \
  --identity-id ${IDENTITY_ID} \
  --logins "${COGNITO_USER_POOL}=${ID_TOKEN}"  --region ap-northeast-1) && echo ${OUTPUT}
```
- 上記のコマンドが正常に通れば、以下のような出力が得られます。
- ※`SessionToken`は本来であればかなり長いですが、見やすくするために短く***にしています。
```bash:出力
{
    "IdentityId": "ap-northeast-1:3a4b18c****************",
    "Credentials": {
        "AccessKeyId": "ASIA*********",
        "SecretKey": "CGByoie0****************",
        "SessionToken": "IQoJb3JpZ2l**********=",
        "Expiration": "2023-04-10T03:21:08+09:00"
    }
}
```
- 得られた
    1. AccessKeyId
    2. SecretKey
    3. SessionToken

    を環境変数として代入します。
```bash:
$export AWS_ACCESS_KEY_ID="ASIA*********"
$export AWS_SECRET_ACCESS_KEY="CGByoie0****************"
$export AWS_SECURITY_TOKEN="IQoJb3JpZ2l**********="
```
- IAMのステータスを以下のコマンドで確認します。
```bash:
$aws sts get-caller-identity
```
- Cognitoから得られたIAMロールであれば以下のように、先ほど作成したIAMロールのArnが表示されます。
```bash:出力
{
    "UserId": "AROASA37*******Y:CognitoIdentityCredentials",
    "Account": "139*******",
    "Arn": "arn:aws:sts::139******:assumed-role/Cognito_dev_id_pool_01Auth_Role/CognitoIdentityCredentials"
}
```

## 最後に
- 元々[AWS Cognito](https://aws.amazon.com/jp/cognito/)というサービスの存在すら知らなかったのでIAMユーザーやSSO以外でAWSのkeyを渡す方法があることに驚きました。
- 私の勉強不足かもしれませんが、今まで触ってきていたAWSのサービスとかなり毛色が違ったので、理解をするのにかなり時間がかかりました。（いまだに理解して使いこなせているのが微妙ですが、、、）
- この記事を書くにあたり参考にした資料等を以下に記載しておりますので、[AWS Cognito](https://aws.amazon.com/jp/cognito/)の理解を深めるために是非ご一読ください。

## 参考資料
https://aws.amazon.com/jp/cognito/

https://aws.amazon.com/jp/rds/

https://docs.aws.amazon.com/ja_jp/cognito/latest/developerguide/cognito-identity.html

https://docs.aws.amazon.com/ja_jp/cognito/latest/developerguide/cognito-user-identity-pools.html

https://dev.classmethod.jp/articles/get-aws-temporary-security-credentials-with-cognito-id-pool-by-aws-cli/

https://www.youtube.com/watch?v=wnB6Fq7-tHs

https://docs.aws.amazon.com/ja_jp/cognito/latest/developerguide/amazon-cognito-user-pools-authentication-flow.html

https://dev.classmethod.jp/articles/sign-up-and-sign-in-by-cognito-with-awscli/

## 個人ブログ

https://takuma-tech.com/2023/04/10/568/