AWS CognitoとサードパーティーIDプロバイダのOIDC連携手順（ユーザープール編）

# はじめに
業務で、AWS Cognitoと外部のIDプロバイダ（以降IdP）をOIDCで連携する検証を行ったのですが、外部IdPを使用した際の記事が少なかったので、具体的な手順に関して書き留めていきたいと思います。
なお、IDプールを使用した際の手順は以下を参照してください：
https://qiita.com/BanaoLihua/items/cda78d265691952a9f12
# CognitoとOIDC
## Cognitoとは
[公式ドキュメント](https://docs.aws.amazon.com/ja_jp/cognito/latest/developerguide/what-is-amazon-cognito.html)には以下のように記されています。
> Amazon Cognito はウェブアプリとモバイルアプリ用のアイデンティティプラットフォームです。これは、ユーザーディレクトリ、認証サーバー、OAuth 2.0 アクセストークンと AWS 認証情報の承認サービスです。Amazon Cognito を使用すると、組み込みのユーザーディレクトリ、エンタープライズディレクトリ、Google や Facebook などのコンシューマー ID プロバイダーからユーザーを認証および承認できます。

要は、様々な認証方式に対応したアプリケーションクライアント向けのアイデンティティプラットフォームということです。今回はその中でもOIDCを使用していきます。

## OIDCとは
OIDCとはOpenID Connectのことで、OAuth2.0の拡張版です。OAuth2.0では認可のみだったのに対し、OIDCでは認証も行うことできます。
詳細については、他に良記事があるので割愛させていただきます。
- [一番分かりやすい OpenID Connect の説明](https://qiita.com/TakahikoKawasaki/items/498ca08bbfcc341691fe)
- [OpenID Connect についてと OAuth2.0 との違いを調べてみた](https://zenn.dev/mryhryki/articles/2021-01-30-openid-connect)

# 外部のIdPを使用した際のシナリオ
公式ドキュメントには、Cognitoと外部IdPでのOIDC連携時には以下のような場面に対応していると記述されています。
- ユーザープールを使用する場合（**今回はこちらを紹介します！**）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1711764/301a8e88-375f-79b5-9d82-770de9a66981.png)

- IDプールを使用する場合
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1711764/c69cb15c-ae5f-3200-8871-a61c9687bdeb.png)

- ユーザープールとIDプールを使用する場合
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1711764/63bc303a-2b22-f0b8-e1ea-32b1fc8a5c94.png)

APIGWではオーソライザーにユーザープールを使用することができます。

# 連携手順
## 前提
以下の準備が揃っていることを前提としています。
- IdPが正常に機能していること
- Cognitoユーザープールでの操作権限を保持していること
## 処理の流れ
![userpool_sequence.drawio.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1711764/73e5d84e-df91-fd6a-8a95-122373cc47c7.png)

この場合はCognitoユーザープールがRPとなります。アプリケーションクライアントはユーザープールの認可エンドポイントへアクセスし、ユーザープールが発行した認可コードがリダイレクト応答によってアプリケーションクライアント側に伝わることになります。

1\.クライアントがCognitoユーザープールの認可エンドポイントへパラメータ付きでアクセス（`https://your_user_pool_domain/oauth2/authorize?【省略】`）
2\.Cognitoが外部IdPの認可エンドポイントへ
3\.ユーザが認証認可
4\.外部IdPが認可コードを発行してCognitoの受け口へ（`https://your_user_pool_domain/oauth2/idpresponse`）
5\.Cognitoが外部IdPのトークンエンドポイントへ
6\.外部IdPがトークンを発行してCognitoの受け口へ
7\.Cognitoが外部IdPのユーザー情報エンドポイントへ
8\.外部IdPがユーザー情報をCognitoの受け口へ
9\.Cognitoは受け取ったユーザ情報をユーザープールに登録
10\.Cognitoは認可コードを発行し、リダイレクト応答を通してクライアントへ

注意すべき点として、**Cognitoの認可エンドポイントおよび認可コードと、外部IdPの認可エンドポイントおよび認可コードは別物である**ことが挙げられる。

## 1．ユーザープールの作成
AWSコンソールからCognitoへアクセスし、ユーザープールを作成していく。
### サインインエクスペリエンスを設定
・プロバイダ名
　☑フェデレーテッドアイデンティティプロバイダ
・Cognitoユーザープールのサインインオプション
　ユーザー名、Eメール、電話番号から選択
・フェデレーテッドサインインのオプション
　☑OpenID Connect（OIDC）

「次へ」を押下
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1711764/a2e6cb6f-2544-2f26-e9dd-7878651fa9db.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1711764/78984c05-9a6d-470c-37e2-997693bef4d4.png)

### セキュリティ要件を設定
・パスワードポリシーモード
　デフォルトかカスタムか選択
・多要素認証
　MFAの有無を選択　※有効にするとCognitoで直接ログインする場合にMFAを求められる
・ユーザアカウントの復旧
　必要に応じて選択

「次へ」を押下

フェデレーションする際にはMFAの有無は関係ないので、無効としてよい。

### サインアップエクスペリエンスを設定
・セルフサービスのサインアップ
　必要に応じて選択
・属性検証とユーザーアカウントの確認
　必要に応じて選択
・必須の属性
  属性を一つ以上選択

「次へ」を押下

### メッセージ配信を設定
・Eメールプロバイダー
　SESもしくはCognitoデフォルトのEメールアドレスを使用するか選択

「次へ」を押下

Cognitoで直接サインインする際、初回でメール配信など行われるが、フェデレーションの場合は基本的に使用しない。

### フェデレーテッドアイデンティティプロバーダを接続
「後で」を押下

### アプリケーションを統合
・ユーザープール名
　任意の名前を入力
・ドメイン
　Cognitoドメインもしくはカスタムドメインを選択し、任意のドメインを入力
・アプリケーションタイプ
　適宜選択
・アプリケーションクライアント名
　任意のクライアント名を入力
・クライアントシークレット
　シークレットの有無を選択
・許可されているコールバックURL
　指定のコールバックURL（今回はConnectに相当するLambdaのURL）を入力

「次へ」を押下し、確認及び作成する。

## 2．外部IdPへのクライアント情報登録
クライアントIDおよびクライアントシークレットが必要となるので、発行しておく必要があります。
なお、作成する際にはリダイレクト可能なURLを指定すると思いますが、ここには先ほど作成したユーザープールの受け口（`https://your_user_pool_domain/oauth2/idpresponse`）を指定します。

発行できたらclient_id、client_secret、scopeをメモをしてください。

## 3．ユーザープールへのIdP情報追加
- **フェデレーティッドサインインのオプション**
☑OpenID Connect（OIDC）
- **プロバイダー名**
任意の名前を入力
- **クライアントID**
前項で取得したclient_idを入力
- **クライアントシークレット**
前項で取得したclient_secretを入力
- **許可されたスコープ**
前項で取得したscopeを入力
- **セットアップ方法**
☑手動入力
- **発行者URL**
発行者のURLはIdPの設定などを確認してください。
ちなみに、IdPから発行されたIDトークンをbase64でデコードした際の`iss`の値が発行者URLに当たります。
- **承認エンドポイント**
IdPの認可エンドポイントのことで、多くの場合は末尾が`/authorization`となります。
- **トークンエンドポイント**
多くの場合は末尾が`/token`となります。
- **ユーザ情報エンドポイント**
多くの場合は末尾が`/userinfo`となります。
- **証明書エンドポイント**
多くの場合は末尾が`/certs`となります。
- **属性マッピング**
ユーザープール属性に対応するOIDC属性を入力

## 4．正常性確認
ユーザープール作成時に、併せて作成したアプリケーションクライアントから簡単に動作確認できます。
アプリケーションクライアントは「アプリケーションの統合」タブ下部に作成されています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1711764/a40a73f5-a07d-b209-7295-799dfcc7f7ec.png)
選択し、「ホストされたUI」から作成したIdPをクリックすると、Cognitoの認可エンドポイントへアクセスされます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1711764/a717f491-bd42-e2e0-68ec-cd2aa35d39aa.png)
認証認可が完了し、遷移先のURLに認可コードのパラメータが付与されています。
![0928.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1711764/4060aafd-2845-13f2-6ae7-c0f4bfbf993b.png)
また、ユーザープールにもユーザが登録されていることを確認します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1711764/b1c0f65b-11c3-9478-aba5-49331a384ab7.png)

# 最後に
外部IdPとユーザープールを使用してアプリケーションクライアントに認可コードを渡すところまで確認できました。アプリケーションクライアントはこの認可コードを使用し、Cognitoのトークンエンドポイントやユーザ情報エンドポイントへアクセスすることで各種情報を取得することができます。
なお、APIGWはオーソライザーとしてCognitoユーザープールにも対応しているため、APIGWで特定のユーザープールを指定し、Lambdaに通すことなどが可能となっています。この辺りは別途記事があるので、そちらを参照してください。