AWS Cognitoを使用してログイン機能を実装する

# はじめに
この記事では、AWS Cognitoを使用して以下の機能の実装方法を説明します。
- メアドとパスワードを使用したサインアップ、ログイン、ログアウト、アカウントの削除
    - CognitoのUIを使用する場合
    - 自分でUIを用意する場合（途中）
- パブリックサービスを使用したサインアップ、ログイン、ログアウト、アカウントの削除（まだ書いてません）

# 前提知識
## フェデレーションとは

https://blog.serverworks.co.jp/tech/2017/07/19/aws-federation/

## ユーザープールとIDプールの違い
CognitoにはユーザープールとIDプールという主な２つのコンポーネントがあります。両者の違いについて公式ドキュメントでは以下のように説明されています。

> ユーザープールでは認証 (アイデンティティの検証) ができます。ユーザープールを使用すると、アプリユーザーはユーザープールを通じたサインインや、サードパーティーのアイデンティティプロバイダー (IdP) を介した連携ができます。
> 
> ID プールでは認可 (アクセスコントロール) ができます。ID プールを使用すると、ユーザーに一意の ID を作成して、他の AWS サービスへのアクセスを許可できます。

https://aws.amazon.com/jp/premiumsupport/knowledge-center/cognito-user-pools-identity-pools/

ユーザープールでまずユーザーが誰なのかをはっきりさせます。例えばこの人はうちのサービスにこの前登録してくれた山田太郎さんというユーザーだからサインインさせてあげよう、あの人はまだ登録してくれてないからまずはサインアップからしてもらおうなどと判断することです。この行為を一般的には認証と呼びます。

IDプールでは、ユーザーが何かしたいと言ってきたときにそれを許可するか判断します。例えばさっき認証した山田太郎さんというユーザーがファイルをアップロードしたいと言ってきたけどこのユーザーにはその権限は与えてないので許可しないようにしよう、田中次郎さんというユーザーがユーザー名を変更したいと言ってきたけど、その権限は与えてるから許可しようなどと判断することです。この行為を一般的には認可と呼びます。

## 一般的なCognitoの使用シナリオ
以下が参考になります。

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

# メアドとパスワードを使用したログイン機能
Cognitoを用いてログイン機能を実装する場合、Cognitoが用意してくれるUI（サインアップ画面やログイン画面など）を使用するか、自分でUIを用意するかを選べます。以下では、両者の設定を説明します。

## ユーザープールの作成
### サインインエクスペリエンスを設定
1. Cognitoのダッシュボードにアクセスし、「ユーザープールを作成」をクリックします。
    ![スクリーンショット 2022-10-30 13.52.08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1639185/ff7f4417-2930-b11b-a44c-175fcacd7403.png)
2. 「Cognitoユーザープールのサインインオプション」の「Eメール」を選択し、「次へ」を押す
    ![スクリーンショット 2022-10-30 14.48.27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1639185/bd0d450b-a7f9-7763-cdf8-5db2a00d09f6.png)

### セキュリティ要件を設定
3. 「パスワードポリシー」はそのままで大丈夫です
    ![スクリーンショット 2022-10-30 14.51.30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1639185/a3f33124-2b41-50a3-2747-9c8af49e0131.png)
4. 「多要素認証」に関しては今回は「MFAなし」にします
    - これはサインイン（ログイン）の際にMFAを必須とするかどうかの設定ですが、そこまでの強度が必要でなければUXを下げるだけなのでMFAは使用しません。
   ![スクリーンショット 2022-10-30 14.53.01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1639185/289cb82e-7fe7-a747-d17c-cf033dcc6acb.png)
5. 「ユーザーアカウントの復旧」はそのままにして、「次へ」を押します
    ![スクリーンショット 2022-10-30 14.58.13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1639185/30798877-441c-fd67-e378-a436b7640b9a.png)

### サインアップエクスペリエンスを設定
6. 「セルフサービスのサインアップ」は有効のままにします
    ![スクリーンショット 2022-10-30 15.06.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1639185/14a7d344-954a-5949-031a-a1c903250384.png)
7. 「属性検証とユーザーアカウントの確認」の「Cognito アシスト型の検証および確認」は、「E メールのメッセージを送信、E メールアドレスを検証」を選択
    ![スクリーンショット 2022-10-30 15.08.54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1639185/1bfc9aa6-a444-872c-e2d2-7cb388bff585.png)
8.  「属性検証とユーザーアカウントの確認」の「属性変更の確認」はそのままにします
    ![スクリーンショット 2022-10-30 15.11.14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1639185/9fca14a5-2ec5-c508-6466-d1ddbfa6f115.png)
9. 「必須の属性」と「カスタム属性」はそのままにして「次へ」を押します
    ![スクリーンショット 2022-10-30 15.12.17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1639185/082f2148-5c23-d6a0-b889-55d79120859e.png)

### メッセージ配信を設定
1. 「Eメール」の「E メールプロバイダー」はCognitoの方を選択します。そのほかもデフォルトのままにして「次へ」を押します
    ![スクリーンショット 2022-10-30 16.03.15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1639185/3dd07cfe-5396-2f39-afc1-304ad6690552.png)
    - Cognitoを使って送信する方法だと、送信できる数に制限があるほか、デフォルトでは`no-reply@verificationemail.com`というメアドからの送信になります
        - 詳細は[このドキュメント](https://docs.aws.amazon.com/ja_jp/cognito/latest/developerguide/user-pool-email.html)が参考になります

### アプリケーションを統合
1. 「ユーザープール名」の「ユーザープール名」には任意のこのユーザープールの名前を入力します。自分のUIを使う場合はチェックを外しましょう
    ![スクリーンショット 2022-10-30 16.19.14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1639185/f0b218ac-ea9c-9e00-7885-1cdb5b8c9ce6.png)
    - CognitoのUIを使用したい場合はチェックを入れ、「ドメイン」の「ドメインタイプ」には「Cognitoドメインを使用する」を選択し、「Cognitoドメイン」には任意のドメインプレフィックスを入力してください
        ![スクリーンショット 2022-10-30 17.55.35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1639185/a76f15c6-6d52-a8ec-e929-4c6c730a1cca.png)
2. 「最初のアプリケーションクライアント」の「アプリケーションタイプ」は「パブリッククライアント」を選択、「アプリケーションクライアント名」は任意の名前を入力、「クライアントのシークレット」は「クライアントのシークレットを生成しない」を選択し、「次へ」を押す
    - Amazon Cognito JavaScriptSDKはクライアントシークレットを使用しないので、例外処理が行われてしまうため、「クライアントのシークレットを生成しない」を選択する[^1]らしいです。自分もよくわかってないんで、誰か教えてください
    ![スクリーンショット 2022-10-30 16.24.34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1639185/48cd4a88-37af-7a49-42ba-0c3b7c678cb2.png)
    - CognitoのUIを使用する人は「クライアントのシークレット」の下に「許可されているコールバックURL」に`http://localhost`と入力しましょう。`https`ではなく、`http`にしてください
        ![スクリーンショット 2022-10-30 18.01.32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1639185/04894b6d-81d5-8276-6eb0-dd10b8f6d2be.png)


### 確認および作成
確認して問題なければ「ユーザープールを作成」を押しましょう。

## IDプールの作成
1. ユーザープール作成後の画面から作成したユーザープール名をクリックし、ユーザープールの詳細画面を表示します。後で必要なので「ユーザープールID」と「クライアントID」（アプリケーションの統合タブに書いてあります）をメモします
1. ユーザープール作成後の画面からIDプールの画面に行くまではちょっとわかりづらいです。まずはユーザープール作成後の画面の上にあるパンくずリストの「Amazon Cognito」をクリックしましょう
    ![スクリーンショット 2022-10-30 16.54.03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1639185/26ed2c8e-5c07-333c-f99f-138fa1858fb7.png)
2. Cognitoのダッシュボードに戻ったら、「ビジネスケースからはじめる」の下のリストから「AWSのサービスへのアクセス権を付与する」という項目を選び、下の「IDプールを作成」を押しましょう
    ![スクリーンショット 2022-10-30 16.56.10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1639185/02e6fa0b-24ce-c350-7687-5016c3a0f7d8.png)
3. 「IDプール名」に任意の名前を入力します
    ![スクリーンショット 2022-10-30 16.59.03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1639185/1db92ad8-8b03-af91-9c50-de0cb7189dcf.png)
4. 「認証プロバイダー」の「Cognito」タブを選択し、先ほどメモした「ユーザープールID」と「クライアントID」をそれぞれ入力し、「プールの作成」を押します
    ![スクリーンショット 2022-10-30 17.10.06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1639185/254fe6b2-b6d5-d32d-f0af-b8d3015b3f52.png)
5. 「許可」を押します
    ![スクリーンショット 2022-10-30 17.12.17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1639185/35f4bcbe-ccbb-738a-a130-425722d7866a.png)

## ログイン機能を実装する
### ログインページのUIを用意する
#### CognitoのUIを使う場合
1. クライアントIDを確認したのと同じ手順で、作成したユーザープールの詳細画面にアクセスして、「アプリケーションの統合」タブを開きます
1. 「アプリケーションクライアント名」に表示されているアプリケーションクライアントをクリックし、詳細画面を開きます
1. 「ホストされたUI」の「ホストされたUIを表示」をクリックします。するとサインイン画面が表示されます。「Sign Up」をクリックします
    ![スクリーンショット 2022-10-30 19.50.20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1639185/6b576d3b-84da-9afb-17b5-4d96c9a2bd10.png)
1. サインアップ画面に遷移するので自分のメアドと任意のパスワードを入力しましょう
    ![スクリーンショット 2022-10-30 19.52.24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1639185/6c960306-301f-083c-c7e1-a36d67de6cf0.png)
1. すると認証コードの入力を求められるのでメールを確認して届いたコードを入力してボタンをクリックします
    - 認証コードが届かない場合は迷惑メールを確認してみてください。私の場合も迷惑メールに入ってました
    - ボタンをクリック後はコールバックURLとして指定した`http://localhost`にリダイレクトすると思うのですが、もちろん`localhost`は動いてないのでエラーメッセージが表示されます。ページを閉じてもう一度「ホストされたUIを表示」をクリックすると同じエラーメッセージが出てくる`localhost`にリダイレクトされるので、ログイン状態が保持されているんだなとわかります
     ![スクリーンショット 2022-10-30 20.02.02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1639185/e626cd91-7417-bc6d-da3d-8b113975e67c.png)

#### 自分でUIを用意する場合
以下の記事が参考になります。

https://qiita.com/fkooo/items/660cab0090a80861155b#%EF%BC%93%E7%94%BB%E9%9D%A2%E5%AE%9F%E8%A3%85-%E3%83%A6%E3%83%BC%E3%82%B6%E7%99%BB%E9%8C%B2%E7%94%BB%E9%9D%A2%E3%82%B5%E3%82%A4%E3%83%B3%E3%82%A2%E3%83%83%E3%83%97%E7%94%BB%E9%9D%A2%E4%BD%9C%E6%88%90

### IDプールに作成したロールにポリシーをアタッチする
1. IDプールに作成したロールの名前を確認する
    - IDプールの詳細画面にアクセスしたいのですが、これまたわかりにくい場所にあります。まずCognitoのダッシュボードを開きます。左側のメニューバー（折り畳まれている場合は開いてください）にある「フェデレーティッドID」をクリックします
        ![スクリーンショット 2022-10-30 20.17.31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1639185/47320d70-5a1b-c344-4bee-a7cb453b15d3.png)
    - すると作成したIDプールが表示されるので、名前部分をクリックしてください
![スクリーンショット 2022-10-30 20.19.58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1639185/f4adfc88-38f6-c9b2-fd52-6f1303e7a3af.png)
    - 右上にある「IDプールの編集」をクリックすると「認証されていないロール」と「認証されたロールの名前」が確認できます
1. 今度はAWSのサービス一覧からIAMのダッシュボードに行って、「ロール」にアクセスすると先ほど確認した２つのロールが表示されているのが確認できます。
1. 認証されたロールの方をクリックして詳細画面に行き、「許可」タブにアクセスして、「許可を追加」 > 「ポリシーをアタッチ」で任意のポリシーをアタッチします

### ユーザープールにカスタムドメインを割り当てる
自分のドメインを持っている場合はそれを割り当てることでログイン機能を自分のサイトに実装できます。ドメインを割り当てる前にRoute53にてDNSレコードを追加する必要があります。

1. Route53のダッシュボードにアクセスします
1. 「ホストゾーン」に行き、ドメイン名をクリックします
1. 「レコード」タブにて、「レコードを作成」をクリックします
1. 「レコード名」に`auth`と入力します
    - [ホストされた UI への独自のドメインの使用](https://docs.aws.amazon.com/ja_jp/cognito/latest/developerguide/cognito-user-pools-add-custom-domain.html#:~:text=%E6%89%80%E6%9C%89%E3%81%99%E3%82%8B%E3%82%A6%E3%82%A7%E3%83%96%E3%83%89%E3%83%A1%E3%82%A4%E3%83%B3,%E4%BD%BF%E7%94%A8%E3%81%97%E3%81%BE%E3%81%99%E3%80%82)で言及されているようにCognitoではルートドメインを使用できず、サブドメインを使う必要があります。特に`auth`というサブドメインを使用するように推奨されています。
1. 「レコードタイプ」は「A」を選択します。
    - Aレコードやその他のDNSレコードについては[この記事](https://www.value-domain.com/media/a-record/)が参考になります
1. ユーザープールのダッシュボードを開き、作成したユーザープールの詳細画面にアクセスし、「アプリケーションの統合」タブを開きます。
1. Cgnitoドメインの削除
1. 「ドメイン」の「アクション」から「カスタムドメインを作成」をクリックします
1. 「カスタムドメイン」に使用したいドメイン名を入力し、「ACM証明書」から証明書を選択し、「カスタムドメインを作成」を押す
    - ACM証明書がない場合は「ACM証明書を作成」を押して作成してください

# パブリックサービスを使用したログイン機能

https://qiita.com/poruruba/items/189945dc64edfe1f2464

  [^1]: [はじめてのCognito! CognitoのログインUIを使って認証システムを実装したまとめ](https://kotororo.net/2018/05/%E3%81%AF%E3%81%98%E3%82%81%E3%81%A6%E3%81%AEcognito-cognito%E3%81%AE%E3%83%AD%E3%82%B0%E3%82%A4%E3%83%B3ui%E3%82%92%E4%BD%BF%E3%81%A3%E3%81%A6%E8%AA%8D%E8%A8%BC%E3%82%B7%E3%82%B9%E3%83%86%E3%83%A0%E3%82%92%E5%AE%9F%E8%A3%85%E3%81%97%E3%81%9F%E3%81%BE%E3%81%A8%E3%82%81/#:~:text=%E3%82%A2%E3%83%97%E3%83%AA%E3%82%AF%E3%83%A9%E3%82%A4%E3%82%A2%E3%83%B3%E3%83%88%E8%A8%AD%E5%AE%9A%E6%99%82%E3%80%81%E3%83%87%E3%83%95%E3%82%A9%E3%83%AB%E3%83%88%E3%81%A7%E3%81%AF%E3%82%AF%E3%83%A9%E3%82%A4%E3%82%A2%E3%83%B3%E3%83%88%E3%82%B7%E3%83%BC%E3%82%AF%E3%83%AC%E3%83%83%E3%83%88%E3%81%AE%E7%94%9F%E6%88%90%E3%81%AB%E3%83%81%E3%82%A7%E3%83%83%E3%82%AF%E3%81%8C%E3%81%A4%E3%81%84%E3%81%A6%E3%81%84%E3%82%8B%E3%81%8C%E3%80%81%E3%81%93%E3%82%8C%E3%82%92%E3%81%AF%E3%81%9A%E3%81%99%E3%80%82%E3%81%93%E3%82%8C%E3%81%AF%E3%80%81Amazon%20Cognito%20JavaScriptSDK%E3%81%AF%E3%80%81%E3%82%AF%E3%83%A9%E3%82%A4%E3%82%A2%E3%83%B3%E3%83%88%E3%82%B7%E3%83%BC%E3%82%AF%E3%83%AC%E3%83%83%E3%83%88%E3%82%92%E4%BD%BF%E7%94%A8%E3%81%97%E3%81%AA%E3%81%84%E3%81%AE%E3%81%A7%E3%80%81%E4%BE%8B%E5%A4%96%E5%87%A6%E7%90%86%E3%81%8C%E8%A1%8C%E3%82%8F%E3%82%8C%E3%81%A6%E3%81%97%E3%81%BE%E3%81%86%E3%81%9F%E3%82%81%E3%80%82)