はじめに
本記事では外部IDプロバイダ(以降IdP)とCognitoアイデンティティプール(以降IDプール)の連携方法について解説します。
なお、ユーザープールでの連携方法に関しては以下をご覧ください。
https://qiita.com/BanaoLihua/items/03b82da7deb109f73361
外部IdPを使用した際のシナリオ
公式ドキュメントには、Cognitoと外部IdPでのOIDC連携時には以下のような場面に対応していると記述されています。
APIGWではオーソライザーにユーザープールを使用することができます。
連携手順
前提
- IdPが正常に機能すること
- IDプール周りの権限を保持していること(※作成に当たってはIAM周りの権限も必要となります)
処理の流れ
外部IdPへのリダイレクト応答およびIDトークン取得、IDプールへのアイデンティティID取得およびAWS一時クレデンシャルの取得はアプリケーション側で実装する必要があります。
1.IDプロバイダの追加
IAMへアクセスし、「アクセス管理」→「IDプロバイダ」→「プロバイダの追加」へ遷移します。
「プロバイダの設定」では以下を設定してください。
・プロバイダのタイプ
☑OpenID Connect
・プロバイダのURL
適宜記入し「サムプリントを取得」を押下
・対象者
クライアントIDを入力
「サムプリントを取得」押下時にエラーが出るもしくは変化が無い場合は、権限が足りていない可能性が高いです。
2.IDプールの作成
Cognitoへアクセスし、「IDプールを作成」へ遷移します。
IDプールの信頼を設定
・ユーザーアクセス
☑認証されたアクセス
・認証されたIDソース
☑OpenID Connect(OIDC)
許可を設定
・IAMロール
新規作成するか、既存のロールを選択
新規作成する場合は、最小限のアクセス許可となり、IAMロール名が必要となる
・IAMロール名(※新規作成する場合のみ)
適宜記入
IDプロバイダーを接続
・OIDC IDプロバイダー
「1.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が取得できます。
// コマンド
aws cognito-identity get-id \
--identity-pool-id "<IDプールのID>" \
--logins '{"<設定したIdP>":"<IDトークン>"}'
IDプールのIDは以下から確認できます。
設定するIdP名はユーザーアクセスタブの「アイデンティティプロバイダー」から確認できます。
// 実行結果
{
"IdentityId": "ap-northeast-1:xxxxxxxxxxxxxxxxxxxxxxxx"
}
AWSクレデンシャルを取得する
// コマンド
aws cognito-identity get-credentials-for-identity \
--identity-id <アイデンティティID> \
--logins '{"<設定したIdP>":"<IDトークン>"}'
// 実行結果
{
"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の認可エンドポイントへリダイレクト応答するようにします。
// 必要な変数を定義
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トークンを取得します。
// 必要な変数を定義
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
// アイデンティティ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);
});