Amazon Cognitoを試してみた（ユーザプール編）

# 背景・目的
私は、現在データエンジニアリングを生業としています。普段は、データ基盤の構築や、パフォーマンスチューニングなどビックデータに関する業務に従事しています。
日頃から、データに関わる業務が多く、Webフロントエンドやバックエンドの技術に触れる機会が少ないため、意図的に学んで行こうと思います。
今回は、Webアプリケーションの認証やユーザ管理のサービスであるAmazon Cognito（以降、Cognitoと言います。）を学んでいこうと思います。

# まとめ
- Cognitoは、簡単な設定でユーザ認証の仕組みを提供する。
- 既存のシステムや他のIdPで管理しているユーザIDを使用することが可能。

# 概要
## Cognitoとは？
> Amazon Cognito を使用すれば、ウェブアプリケーションおよびモバイルアプリに素早く簡単にユーザーのサインアップ/サインインおよびアクセスコントロールの機能を追加できます。Amazon Cognito は、数百万人のユーザーにスケールし、Apple、Facebook、Google、Amazon などのソーシャル ID プロバイダー、SAML 2.0 および OpenID Connect によるエンタープライズ ID プロバイダーを使用したサインインをサポートします。 

- 対象はウェブ、モバイルアプリ
- 機能は、認証、承認、ユーザ管理
- サインインは、ユーザ名とパスワードの直接か、サードパーティを通じてサインイン可能。
- SAML2.0、OpenID ConnectによるエンタープライズIDはプロバイダーを使用したサポートしている。

> Amazon Cognito の主な 2 つのコンポーネントは、ユーザープールと ID プールです。ユーザープールは、アプリユーザーのサインアップとサインインオプションを提供するユーザーディレクトリです。ID プールは、AWS の他のサービスに対するアクセスをユーザーに許可します。ID プールとユーザープールは別々に使用することも、一緒に使用することもできます。


- 以下の2つのコンポーネントで構成されます。個別に利用、一緒に利用も可能です。
    - ユーザプール
        - ユーザディレクトリ
        - アプリユーザのサインアップ、サインインオプションを提供する。
    - IDプール
        - AWSの他のサービスに対するアクセスをユーザに許可する。

### Amazon Cognito のユーザープールと ID プールの併用
認証から、AWSサービスにアクセスまでの流れを記載します。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/09805f26-90f2-7a71-df92-5a80da9a2276.png" width="500">
※ 出典：[Amazon Cognitoとは？](https://docs.aws.amazon.com/ja_jp/cognito/latest/developerguide/what-is-amazon-cognito.html)から抜粋しています。

1. アプリのユーザーはユーザープールを介してサインインし、認証が成功するとユーザープールトークンを受け取ります
1. アプリケーションが ID プール経由でユーザープールトークンを AWS 認証情報と交換します。
1. アプリケーションユーザーがこれらの AWS 認証情報を使用して、Amazon S3 または DynamoDB などの AWS のその他サービスにアクセスできるようになります。

## Amazon Cognito の機能
### ユーザープール
> ユーザープールは、Amazon Cognito のユーザーディレクトリです。ユーザープールを使用することで、ユーザーはウェブまたはモバイルアプリに Amazon Cognito 経由でサインインする、またはサードパーティー ID プロバイダー (IdP) 経由でフェデレートすることができます。ユーザーが直接またはサードパーティーを通じてサインインするかどうかにかかわらず、ユーザープールのすべてのメンバーには、SDK を通じてアクセスできるディレクトリプロファイルがあります。

ユーザープールは次の機能を提供します。
- サインアップおよびサインイン。
- ユーザーをサインインするためのカスタマイズ可能な組み込みWeb UIの提供
- Facebook、Google でのソーシャルサインイン、Login with Amazon、Sign in with Apple、ユーザープールからの SAML および OIDC ID プロバイダー経由のサインイン。
- ユーザーディレクトリとユーザープロファイルの管理。
- 多要素認証 (MFA) などのセキュリティ機能、漏洩した認証情報のチェック、アカウントの乗っ取りからの保護、電話とEメールによる検証。
- カスタマイズされたワークフローと AWS Lambda トリガーによるユーザー移行。

> Amazon Cognito ユーザープールは、何百万人ものユーザーにスケールするセキュアな ID ストアを提供します。Cognito ユーザープールは、インフラストラクチャのプロビジョニングを行うことなく、より簡単に設定することができ、ユーザープールのすべてのメンバーは、ソフトウェア開発キット (SDK) を通じて管理できるディレクトリプロファイルを持っています。

- スケールするセキュアなIDストアを提供


### ID プール
> ID プールを使用すると、ユーザーは Amazon S3 や DynamoDB などの AWS のサービスにアクセスするための一時的な AWS 認証情報を取得できます。

- AWSサービスにアクセス可能な一時的なクレデンシャルを取得できる。

> ID プールは、匿名ゲストユーザーと、ID プールのユーザーを認証するのに使用できる次の ID プロバイダーをサポートします。

IdPの一覧。
- Amazon Cognito user pools
- Facebook、Google でのソーシャルサインイン、Login with Amazon、Sign in with Apple
- OpenID Connect (OIDC) プロバイダー
- SAML ID プロバイダー
- デベロッパーが認証した ID



## 一般的な Amazon Cognito シナリオ
6つの一般的なシナリオを以下に記載します。

### ユーザープールを使用して認証する
ユーザーがユーザープールを使用して認証できるようにすることが可能。
アプリユーザーは、ユーザープール経由で直接サインインする、またはサードパーティーの ID プロバイダー (IdP) 経由でフェデレートする事が可能。

認証が正常に行われると、ウェブまたはモバイルアプリがCognito からユーザープールトークンを受け取る。
このトークンは、アプリケーションがAWSのその他サービスにアクセスできるようにする AWS認証情報の取得に使用できる。
その他にサーバー側のリソースやAPIGateway へのアクセスを制御するために使用することも可能。

<img width="400" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/9300e39d-bb8b-cdd0-58e2-6bc38ccdfc39.png">
※ 出典：[一般的な Amazon Cognito シナリオ](https://docs.aws.amazon.com/ja_jp/cognito/latest/developerguide/cognito-scenarios.html)から抜粋。

### ユーザープールを使用してサーバー側のリソースにアクセスする
サインインが正常に行われると、ウェブまたはモバイルアプリがCognito からユーザープールトークンを受け取る。
サーバ側へのアクセスを制御するには、このトークンを利用する。
また、ユーザプールグループを作成して許可を管理したり、異なるタイプのユーザを管理したりできる。

<img width="300" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/426da96f-9d79-2f36-70d5-17115422bfd0.png">

※ 出典：[一般的な Amazon Cognito シナリオ](https://docs.aws.amazon.com/ja_jp/cognito/latest/developerguide/cognito-scenarios.html)から抜粋。


ユーザプールのドメインを設定するとCognitoがサインインアップ、サインインするページを追加できるHosted Web UIをプロビジョニングする。
このOAuth2.0認証基盤を利用すると、独自のリソースサーバを作成してユーザは保護されたリソースにアクセスできる。


### ユーザープールと共に API Gateway と Lambda を使用してリソースにアクセスする
ユーザーが API Gateway 経由で API にアクセスできるようにすることが可能。
APIGateway は、正常に行われたユーザープール認証からのトークンを検証し、これらのトークンを Lambda関数などのリソース、または独自のAPIへのアクセス権をユーザーに付与するために使用する。

ユーザープール内のグループは、グループメンバーシップを IAM ロールにマップすることによって、API Gateway でアクセス許可を制御するために使用可能になる。

CognitoオーソライザーのLambda関数による検証のために、APIGatewayへのリクエストでユーザープールトークンを送信する事が可能。

<img width="400" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/0ba532a5-ec4d-a924-5227-625d5681eaa6.png">

※ 出典：[一般的な Amazon Cognito シナリオ](https://docs.aws.amazon.com/ja_jp/cognito/latest/developerguide/cognito-scenarios.html)から抜粋。


### ユーザープールと ID プールを使用して AWS のサービスにアクセスする
ユーザープールの認証後に、アプリケーションがCognito からユーザープールトークンを受け取り、IDプールを使用したAWSの他のサービスへの一時的なアクセス権と交換できる。
<img width="500" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/93b95e03-e92e-7b67-fcdf-a2c19ac5042a.png">
※ 出典：[一般的な Amazon Cognito シナリオ](https://docs.aws.amazon.com/ja_jp/cognito/latest/developerguide/cognito-scenarios.html)から抜粋。

### サードパーティーで認証を行い、ID プールを使用して AWS サービスにアクセスする
ユーザーが IDプール経由でAWSのサービスにアクセスできるようにすることが可能。
ID プールには、サードパーティー ID プロバイダーによって認証されたユーザーからの IdP トークンが必要。※匿名ゲストの場合は何も必要なし。
IDプールは、AWSの他のサービスへのアクセスに使用できる一時的なAWS認証情報を付与する。
<img width="400" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/1327f79b-db32-e7c3-9638-3285960060b7.png">
※ 出典：[一般的な Amazon Cognito シナリオ](https://docs.aws.amazon.com/ja_jp/cognito/latest/developerguide/cognito-scenarios.html)から抜粋。

### Amazon Cognito を使用して AWS AppSync リソースにアクセスする
正常に行われたCognito 認証 (ユーザープールまたは ID プール)からのトークンを使用して、AppSyncへのアクセス権をユーザーに付与することが可能。

<img width="400" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/41b303bd-b182-cac6-51f0-32d00b44c66b.png">

※ 出典：[一般的な Amazon Cognito シナリオ](https://docs.aws.amazon.com/ja_jp/cognito/latest/developerguide/cognito-scenarios.html)から抜粋。


# 実践
[Amazon Cognito チュートリアル](https://docs.aws.amazon.com/ja_jp/cognito/latest/developerguide/tutorials.html)を元に試してみます。

## ユーザプール
### ステップ 1. ユーザープールを作成します。
1.Cognitoのマネコンで「ユーザプールの管理」をクリックします。
<img width="400" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/70fc0bd4-5036-f625-e65a-78f69672748b.png">

2.「ユーザプールの作成」をクリックします。
<img width="400" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/132b5890-ff7f-e956-8a81-51894705f7bf.png">

3.プール名に「TestUserPool」を入力後、「デフォルトを確認する」をクリックします。
<img width="400" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/b08a5913-c6c6-9f82-f6d7-994cf148fd1c.png">


4.画面左のナビゲーションペインにある「属性」をクリックします。
<img width="400" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/9aaaffa4-20e7-16cf-8df8-46c5f80c7a25.png">

5.以下を選択し、「次のステップ」をクリックします。
- 「Eメールアドレスおよび電話番号」のラジオボタン
- 「Eメールアドレスを許可」のラジオボタン
<img width="400" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/88aee78a-3108-c991-0246-76a6f586bc05.png">

6.画面左のナビゲーションペインにある「確認」をクリックします。
<img width="400" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/8344283f-eb86-d167-d302-6142915fc909.png">

7.「プールの作成」をクリックします。
<img width="600" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/6ab0e372-c1a9-a540-8f61-57879bf3ca8f.png">

8.ユーザプールの管理画面に表示されました。
<img width="400" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/58b5e7d6-f05f-bb01-a344-f0549d5245df.png">

### ステップ 2. アプリケーションを追加して、ホストされたウェブの UI を有効にする
1.ユーザプール管理画面で、「TestUserPool」をクリックします。
<img width="400" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/58b5e7d6-f05f-bb01-a344-f0549d5245df.png">

2.画面左のナビゲーションペインにある「アプリクライアント」をクリックします。
<img width="200" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/f8b03a2f-a6dc-dc9f-4439-c5dc9ebf109a.png">

3.「アプリクライアントの追加」をクリックします。
<img width="600" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/60219550-eeff-0eee-61e9-30aff02e212d.png">

4.以下を設定し、「アプリクライアンの作成」をクリックします。
- アプリクライアント名に「TestAppClient」を入力
- 「クライアントシークレットを生成」のチェックボックスをオフ（※1）
<img width="600" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/d75c8a49-04e0-7448-471f-45d40050676a.png">
<img width="600" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/69eff37d-854f-5532-0694-5458b46c864f.png">


※1 理由は、以下のとおりです。
> この演習では、[Generate client secret] (クライアントシークレットを生成する) オプションをオフにします。この演習で使用する JavaScript など、クライアント側の認証でクライアントシークレットを使用することは安全ではなく、本番アプリケーションケーションクライアントには推奨されません。クライアントシークレットは、アプリケーションのサーバー側の認証コンポーネントでクライアントシークレットを保護できる場合にのみ使用します。

5.アプリクライアントIDが表示されるので、書き留めて「プールの詳細に戻る」をクリックします。

6.画面左のナビゲーションペインにある「アプリクライアントの設定」をクリックします。
<img width="400" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/fdb1fb9d-57ab-62ff-0c02-2265e7699803.png">

7.以下を入力します。
- 有効なIDプロバイダ「Cognito User Pool」を選択
- コールバックURL
    - ユーザーが認証された後で呼び出す画面
- サインアウトURL
    - サインアウト後に呼び出される画面
<img width="600" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/f1782c47-d637-6e9b-8874-a35430098494.png">


8.以下を入力し、「変更を保存」をクリックします。
- 許可されているOAuthフロー
    - Authorization code grant
    - Implicit grant
- 許可されているOAuthスコープ
    - phone
    - email
    - openid
    - aws.cognito.signin.user.admin
    - profile

<img width="600" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/61f1b49c-3bff-7123-e8d3-27c1181554cd.png">


> [Authorization code grant (認証コードの付与)] を選択して、ユーザープールトークンと交換される認証コードが返されます。トークンはエンドユーザーに直接公開されないため、侵害される可能性は低くなります。ただし、ユーザープールトークンの認証コードを交換するために、バックエンドでカスタムアプリケーションが必要です。セキュリティ上の理由から、モバイルアプリ用の認証コード付与フローとコード交換用証明キー (PKCE) を併用することをお勧めします。

> [Allowed OAuth Flows] (許可されている OAuth フロー) で [Implicit grant] (暗黙の付与) を選択し、Amazon Cognito からユーザープールの JSON Web トークン (JWT) が返されるようにします。このフローは、トークンの認証コードを交換できるバックエンドがない場合に使用でき、トークンのデバッグにも役立ちます。

9.画面左のナビゲーションペインにある「ドメイン名」をクリックします。
<img width="200" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/13ed6455-bad8-33f1-0624-4f9ea738cdf4.png">

10.ドメイン名を入力し、使用可能かチェックし、最後に「変更を保存」をクリックします。
<img width="600" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/e13ac1e4-22e7-660e-423c-1b29377e8117.png">

11.画面左のナビゲーションペインにある「アプリクライアントの設定」をクリックします。
<img width="200" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/1b72f62e-a055-155f-fd25-b98ee2f28577.png">

12.「ホストされたUIを起動」をクリックします。
<img width="400" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/87401757-76d3-4048-a7f7-0db3c5780527.png">

13.ログインページが表示されるので、サインアップをクリックします。
<img width="400" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/de3064d6-3291-86ad-bb4f-3e72c7f0eb2b.png">

14.指定したemailアドレスに、verification codeが送られるので入力します。
<img width="400" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/6ccf942b-6fa4-ae25-dcad-15201e3ab64d.png">

15.コールバックURLに指定したHTMLが表示されました。（表示されているのは、自分で用意した適当な画面です。）
<img width="400" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/d178ef0c-d142-8b5f-75cb-e7188c1f53f5.png">

16.全般を確認すると、推定ユーザ数が1になりました！
<img width="600" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/5f27aded-2070-2a8a-78d1-a0ad51ce318e.png">

17.ユーザとグループで、ユーザが1件登録されていました！
<img width="600" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/ce1ec808-7b0d-418a-0859-51c4354075ce.png">

18.ログアウトのURLにアクセスします。（表示されているのは、自分で用意した適当な画面です。）
<img width="400" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/1080dc61-49bb-56ff-eeb0-21490243b0d3.png">


# 考察
今回は、Cognito内にアイデンティティストアを用意しました。次回は、IdPと連携した場合を試してみます。

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

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

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

https://github.com/aws/aws-sdk-js/releases

https://docs.aws.amazon.com/sdk-for-javascript/v2/developer-guide/getting-started-browser.html