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

# はじめに
本記事では外部IDプロバイダ（以降IdP）とCognitoアイデンティティプール（以降IDプール）の連携方法について解説します。
なお、ユーザープールでの連携方法に関しては以下をご覧ください。
https://qiita.com/BanaoLihua/items/03b82da7deb109f73361
# 外部IdPを使用した際のシナリオ
公式ドキュメントには、Cognitoと外部IdPでのOIDC連携時には以下のような場面に対応していると記述されています。

- IDプールを使用する場合（**今回はこちらを紹介します！**）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1711764/7454d73b-e8d8-da86-0630-70ac6ec46ca6.png)

- ユーザープールを使用する場合
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1711764/f646d247-cfc4-07e0-9f1e-07db309e80ee.png)

- ユーザープールとIDプールを使用する場合
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1711764/b0709593-09b8-70fc-ee47-70a75da4c7aa.png)

APIGWではオーソライザーにユーザープールを使用することができます。
# 連携手順
## 前提
- IdPが正常に機能すること
- IDプール周りの権限を保持していること（※作成に当たってはIAM周りの権限も必要となります）
## 処理の流れ
![処理の流れ.drawio (3).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1711764/6df1720e-e4ab-11ae-3d04-e61febdb58fb.png)

外部IdPへのリダイレクト応答およびIDトークン取得、IDプールへのアイデンティティID取得およびAWS一時クレデンシャルの取得はアプリケーション側で実装する必要があります。

## 1．IDプロバイダの追加
IAMへアクセスし、「アクセス管理」→「IDプロバイダ」→「プロバイダの追加」へ遷移します。
「プロバイダの設定」では以下を設定してください。

・プロバイダのタイプ
☑OpenID Connect
・プロバイダのURL
適宜記入し「サムプリントを取得」を押下
・対象者
クライアントIDを入力
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1711764/14b7bb44-b1a0-72bd-e4f5-6db2616c7c92.png)
「サムプリントを取得」押下時にエラーが出るもしくは変化が無い場合は、権限が足りていない可能性が高いです。

## 2．IDプールの作成
Cognitoへアクセスし、「IDプールを作成」へ遷移します。
### IDプールの信頼を設定
・ユーザーアクセス
☑認証されたアクセス
・認証されたIDソース
☑OpenID Connect（OIDC）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1711764/98f43f8b-ac8f-0f8b-b312-a3996ef3cb08.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1711764/1155ceb2-6f9d-18f8-6c91-2dc7836e064a.png)

### 許可を設定
・IAMロール
新規作成するか、既存のロールを選択
新規作成する場合は、最小限のアクセス許可となり、IAMロール名が必要となる
・IAMロール名（※新規作成する場合のみ）
適宜記入
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1711764/f056b109-97a1-bf26-761e-7b1e8cd7ea7d.png)

### IDプロバイダーを接続
・OIDC IDプロバイダー
「１．IDプロバイダ追加」で設定したIdPを選択
・ロール設定
☑デフォルトの認証されたロールを使用
・アクセスコントロールの属性
☑非アクティブ

### プロパティを設定
・IDプール名
適宜記入
・基本認証
☐基本フローをアクティブ化

## 3．正常性確認
正常に作成したIDプールが機能しているか確認していきます。
アプリケーションで処理を作りこむ際はAWS SDKを使用する必要がありますが、以下の言語が対応しています。
- .NET
- Go
- Java
- JavaScript
- Python
- Ruby
- PHP
- C++

今回はJavaScriptを例に一部紹介します。

### AWS CLIで確認する場合
#### 認可コードの取得
外部IdPの認可エンドポイントへアクセスして認可コードを取得します。
#### IDトークンの取得
認可コードを使用して外部IdPのトークンエンドポイントへアクセスし、IDトークンを取得します。
#### IDプールでアイデンティティIDを取得する
以下を実行するとアイデンティティIDが取得できます。
```shell
// コマンド
aws cognito-identity get-id \
--identity-pool-id "＜IDプールのID＞" \
--logins '{"＜設定したIdP＞":"＜IDトークン＞"}'
```
IDプールのIDは以下から確認できます。
![1005.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1711764/68c57067-cf30-5ec1-22e5-dc2867c16f61.png)
設定するIdP名はユーザーアクセスタブの「アイデンティティプロバイダー」から確認できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1711764/7befc5e2-b04e-a15a-114b-b56bc41210e3.png)
```shell
// 実行結果
{
    "IdentityId": "ap-northeast-1:xxxxxxxxxxxxxxxxxxxxxxxx"
}

```
#### AWSクレデンシャルを取得する
```shell
// コマンド
aws cognito-identity get-credentials-for-identity \
--identity-id ＜アイデンティティID＞ \
--logins '{"＜設定したIdP＞":"＜IDトークン＞"}'
```
```shell
// 実行結果
{
    "IdentityId": "ap-northeast-1:xxxxxxxxxxxxxxxxxxxxxxxx",
    "Credentials": {
        "AccessKeyId": "xxxxxxxxxxxx",
        "SecretKey": "xxxxxxxxxxxx",
        "SessionToken": "xxxxxxxxxxxx",
        "Expiration": "2023-01-01T00:00:00+00:00"
    }
}
```
以上、AWS CLIでの一時クレデンシャル発行でした。
### アプリケーションで実装する場合（Lambdaでnode.jsを使用）
#### 認可コードの取得
アプリクライアントにアクセスする際、認可コードがパラメータに付与されていない状態だと外部IdPの認可エンドポイントへリダイレクト応答するようにします。
```js
// 必要な変数を定義
const authorization_endpoint = "＜認可エンドポイント＞";
const response_type = "code";
const client_id = "＜クライアントID＞";
const redirect_uri = "＜リダイレクト先（アプリクライアント）＞";
const scope = "＜スコープ＞";
// 認可エンドポイントへリダイレクト応答
const redirect_response = {
  statusCode: 301,
  headers: {
    location: `${authorization_endpoint}?response_type=${response_type}&client_id=${client_id}&redirect_uri=${redirect_uri}&scope=${scope}`
  },
};
return redirect_response;
```
#### IDトークンの取得
取得した認可コードを使用してトークンエンドポイントへアクセスし、IDトークンを取得します。
```js
// 必要な変数を定義
const token_endpoint = "＜Trustbindのトークンエンドポイント＞";
const grant_type = "authorization_code";
const redirect_uri = "＜リダイレクト先（アプリクライアント）＞";
const client_id = "＜クライアントID＞";
const client_secret = "＜クライアントシークレット＞";
const code = ＜認可コード＞;

// リクエストボディに変数を追加してトークンエンドポイントへPOSTリクエスト（fetch）
const params = new URLSearchParams();
params.append('grant_type', grant_type);
params.append('redirect_uri', redirect_uri);
params.append('client_id', client_id);
params.append("client_secret", client_secret);
params.append("code", event.queryStringParameters.code);
const postResponse = await fetch(token_endpoint, {
  method: "POST",
  body: params,
});

// レスポンスを整形してid_tokenのみ抜き出し
const data = await postResponse.json();
const id_token = JSON.parse(JSON.stringify(data)).id_token;
```
#### アイデンティティIDおよび一時クレデンシャルの取得
AWS SDKからCLIで行った操作と同じことができます。以下はJavaScriptの場合です。
詳細はAPIリファレンスのCognitoアイデンティティプールの項目を参照してください：
https://docs.aws.amazon.com/ja_jp/cognitoidentity/latest/APIReference/API_Operations.html
```js
// アイデンティティID取得
var params = {
  IdentityPoolId: "＜IDプールのID＞" // ※必須
  AccountId: ＜アカウントID＞,
  Logins: {
    "＜IDプールに登録したIdP名＞": "＜IDトークン＞"
  }
};
cognitoidentity.getId(params, function(err, data) {
  if (err) console.log(err, err.stack); 
  else     console.log(data);
});

// AWSクレデンシャル交換
var params = {
  IdentityId: "＜アイデンティティID＞", // ※必須
  CustomRoleArn: "＜カスタムロールのARN＞",
  Logins: {
    "＜IDプールに登録したIdP名＞": "＜IDトークン＞"
  }
};
cognitoidentity.getCredentialsForIdentity(params, function(err, data) {
  if (err) console.log(err, err.stack);
  else     console.log(data);
});
```