お久しぶりです。本記事はBeeX Advent Calendar 2025の1日目の記事です。
はじめに
Amazon Cognito(以下Cognito)や認証認可周りを何度も勉強&忘れるを繰り返してきました。この負の連鎖を断ち切る、あるいはまた忘れたときに帰ってこれる場所が欲しくなり、本記事の執筆に至りました。なので内容的にはほぼ自分用の記事なのですが、一応想定読者は以下の通りです。
- 認証認可周りの用語で混乱している人
- Cognitoがイマイチよくわからない人
- ユーザープールは認証、IDプールは認可で使うとなんとなく覚えている人
筆者は認証認可のスペシャリストではございません。可能な限り調査・検証したうえで記事化しておりますが、もしこの分野に詳しい方で内容的に正しくない、あるいは誤解を招く表現になっていると判断いただいた際は大変お手数ですがコメントでご指摘いただけますと幸いです。
前提知識
認証認可周りの用語
認証
ユーザーに対し「あなたは誰ですか?」と確認する作業です。Cognitoだとユーザープールがこの機能を提供します。
認可
ユーザーに対しどんなことができるか権限を与える作業です。こちらもCognitoだとユーザープールがこの機能を提供します。IDプールではないです。
An Amazon Cognito user pool is a user directory for web and mobile app authentication and authorization.
OAuth 2.0
標準化団体であるInternet Engineering Task Force (IETF)のページによると「サードパーティのアプリケーションが HTTP サービスへ限定的なアクセスを得られるようにする仕組み(フレームワーク)」です。以下引用文です。
The OAuth 2.0 authorization framework enables a third-party application to obtain limited access to an HTTP service, either on behalf of a resource owner by orchestrating an approval interaction between the resource owner and the HTTP service, or by allowing the third-party application to obtain access on its own behalf. This specification replaces and obsoletes the OAuth 1.0 protocol described in RFC 5849.
(自動翻訳:OAuth 2.0 認可フレームワークは、サードパーティのアプリケーションが HTTP サービスへ“限定的なアクセス”を得られるようにする仕組みです。これは、リソース所有者(resource owner)と HTTP サービスのあいだで承認のやり取りを取り持って所有者に代わってアクセスさせる場合と、アプリケーション自身の名義でアクセスを得る場合の両方に対応します。本仕様は、RFC 5849 に記された OAuth 1.0 を置き換え、廃止します。)
AWSのBlackbeltからも引用します。
・リソースへのアクセス権を委譲するための認可プロトコル
・アクセス権はアクセストークンの形式で発行される
OAuth2.0を用いることで、サードパーティアプリケーションに接続先のIDやパスワードを渡さないで、必要な権限のみを与えることができます。権限自体も簡単にはく奪可能なのでいざという時も便利です。
具体例を用いてみていきましょう。例えばあるユーザーが家計簿アプリを用いて、銀行口座のデータを連携して帳簿をつけるとしましょう。この時の登場人物は下記の通りです。
- リソースオーナー:リソース所有者です
- クライアント:リソースオーナーから権限移譲され、リソースサーバーを使うアプリです。今回の例では認証情報を安全に管理できるコンフィデンシャルクライアントであることを前提とします
- 認可サーバー:リソースオーナーを認証後、クライアントに権限移譲をしていいか確認を取ってアクセストークンをクライアントに発行します
- リソースサーバー:データを提供するサーバーでWeb APIの形式で提供されます
上記登場人物に加えて以下のワードも重要なので抑えましょう。
認可コード
リソースオーナーがクライアントへの権限移譲を同意した証として発行され、ブラウザを介してクライアントに届きます。アクセストークン取得のために必要です。発行された認可コードは短期間(最大10分程度推奨)で無効になり、一度利用した認可コードは再利用できません。また、同じ認可コードが2回以上使われた場合、認可サーバーはそのリクエストを拒否し、その認可コードを元に発行されたトークンも無効化すべきとされています。登場しないグラントタイプ(クライアントがアクセストークンを取得する流れ)もありますが、本記事では省略します。
アクセストークン
与えられた権限(スコープ)の範囲内でリソース操作を可能にする鍵のようなものです。アクセストークンのフォーマットは利用するサービスに依存しており、たとえばCognitoであればJWT方式が利用されます。注意すべき点としては、OAuthで発行される多くのアクセストークンは利用者が誰向けに発行されたものか確認しない、認証無しで誰でも使えてしまうBearerトークンであることです。AWSのBlackbeltからも引用します。
・OAuth2.0で定義される
・ユーザーの権限による代理アクセスとして主に認可の目的で使用される
>Scopeが設定され、Scopeに基づいたアクセスが認可されていることを示す
>リソースサーバーにアクセストークンを提示することで、リソースアクセスを認可する
リフレッシュトークン
実装するか否かはオプションで、認可サーバーが決定します。アクセストークンを再発行する際に用いるもので、クライアントから認可サーバーに対して発行されます。
OAuthの流れ
以上がOAuth2.0の基本用語説明です。では、ユーザーが家計簿アプリを用いて、銀行口座のデータを連携する流れを見ていきましょう。ざっくり以下の通りです。
OAuth2.0を利用するメリットについて触れます。OAuth2.0を用いない場合、家計簿アプリ(クライアント)に銀行のAPI群(リソースサーバー)のIDとパスワードを教える必要があります。しかしそうなるとユーザー(リソースオーナー)が銀行のAPI群(リソースサーバー)に対してできるあらゆる操作が、家計簿アプリ(クライアント)も可能になります。もしクライアント(家計簿アプリ)に悪意があったら銀行情報を見るに留まらず、例えばあらぬ口座に勝手に送金されることもあり得ます。しかしOAuth2.0を使えば上記のように認証情報をIDとパスワードを共有せず、権限を絞ったアクセストークンの形で払い出されるため、仮にクライアント(家計簿アプリ)に悪意があっても限られた操作しかできません。委譲される権限も➂で確認できます。IDとパスワードを共有せず、必要な権限のみ家計簿アプリ(クライアント)に与えられる。これがOAuth2.0を用いるメリットです。権限自体も簡単に無くすことができます。
ここで⑤~⑦をみて、「なぜわざわざ渡した認可コードを再度受け取ってアクセストークンを発行しているんだ?認可コードではなくいきなりアクセストークンを渡しちゃダメなのか?」と思われる方がいるでしょう。これは⑤の認可コードのやりとりがブラウザ上でやり取りされ、盗聴のリスクがあったり、ブラウザ上に保存されたデータは悪意のある第三者からXSSのような攻撃で盗まれる可能性があるためです。以下はWorkshop時のサンプルです。なんとなく「ここで認可コードでなく、アクセストークンを直接やりとりするのはまずそうだな…」と直感的に感じていただけると思います。
認可コード自体はアクセストークンと引き換えない限りは特に意味のあるものではない、かつ有効期限も短く一度使えば再利用不可です。また、説明は省略しますが、認可コードを奪われないためのPKCEという仕組みもあります。以上から、ここで敢えてアクセストークンではなく認可コードをやり取りしているわけです。⑥と⑦についてはブラウザを介さず、外部からアクセスできないバックエンドが処理するため、前述のリスク抑制につながります。
OAuthで用いるエンドポイント
上のフロー図からは省いていますが、どれも重要ですので抑えておきましょう。
- 認可エンドポイント(認可サーバーが提供):リソースオーナーの認証を行うエンドポイントです。認証完了後はリソースオーナーがアクセス権限委譲を許可したら、その証として認可コードがリダイレクトエンドポイントへ送られます
- リダイレクトエンドポイント(リダイレクトURI。クライアントが提供):認可サーバーから認可コードを受け取るエンドポイントです。クエリパラメータの形で認可コードを受け取ります
- トークンエンドポイント(認可サーバーが提供):認可コードを受け取ったクライアントが、アクセストークンと引き換えるエンドポイントです
OpenID Connect 1.0(OIDC)
OAuth 2.0をベースとした、認証のために用いられるフレームワークです。標準化団体であるOpenID Foundationのページからも引用します。
OpenID Connect 1.0 is a simple identity layer on top of the OAuth 2.0 protocol. It enables Clients to verify the identity of the End-User based on the authentication performed by an Authorization Server, as well as to obtain basic profile information about the End-User in an interoperable and REST-like manner.
(自動翻訳:OpenID Connect 1.0 は、OAuth 2.0 の上に載るシンプルな“IDレイヤ”です。クライアントは、認可サーバーが実施した認証にもとづいてエンドユーザーの本人性を検証でき、あわせてエンドユーザーの基本的なプロフィール情報を、相互運用可能で REST 風のやり方で取得できます。)
AWSのBlackbeltからも引用します。
・ユーザーのログイン状態やID情報を安全に共有するための認証結果の流通プロトコル
・ユーザー情報はIDトークンの形式で発行される
・OAuth2.0を拡張したプロトコル
Auth屋さんの書籍の表現もわかりやすかったので引用します。仕組みの観点だと以下のように表現できるようです。
OIDC = OAuth + IDトークン + UserInfoエンドポイント
また、OIDCではOAuthの時とは異なる登場人物の呼び方があるのでそちらも紹介します。
| OAuth | OIDC |
|---|---|
| リソースオーナー | エンドユーザー |
| クライアント | リライング・パーティ |
| 許可サーバー | IDプロバイダー |
| リソースサーバー | UserInfoエンドポイント |
IDトークン
AWSのBlackbeltから引用します。
・OIDCで定義される
・ある時点でのユーザーのアイデンティティ情報を表し、主に認証を目的として使用される
>認証済みであるユーザーのアイデンティティ情報が含まれる
>サービスがIDトークンを検証することで、そのサービスへのログインを許可する
ユーザーが誰であるか特定する身分証のようなもので、OIDCで登場します。中身はJSON Web Token(JWT)形式のデータ構造です。以下の三つのパートから成り立っています
-
ヘッダー
トークンのメタデータが入っています。JSONの中身は以下の通りです。- typ:オブジェクトのタイプを示す。必須ではなく省略されることもあります
- alg:トークンがどのようなアルゴリズムで署名されているか記載されます。RS256を使うのが一般的みたいです
- kid:非対称暗号方式で用いる公開鍵のIDを示します
-
ペイロード
トークンのclaims(ユーザーや情報やトークンに関する情報の集まり)が入っています。JSONの中身は以下の通りです。- aud:Audience。IDトークンを受け取るクライアントのクライアントIDが入ります。このID以外のクライアントがトークンを利用することはできません
- exp:Expiration time。IDトークンの有効期限を示します
- iat:Issued At。IDトークンの発行時間を示します
- iss:Issuer。IDトークンの発行者を示し、URLが入ります
- jti:JWT ID。トークンを一意に識別するIDです
- nbf:Not Before。トークンがどの時点から有効かを示します
- sub:Subject。誰向けのトークンかを示します。リライング・パーティはこの値を参考にしてユーザーを識別します
-
署名
IDトークン情報の改ざん防止が目的で、ヘッダーとペイロードに対し、秘密鍵でデジタル署名した値です。公開鍵を用いて検証することで改ざんされていないことと発行者を確認できます。
UserInfo エンドポイント
こちらはAuth屋さんの書籍の説明が一番わかりやすかったので、こちらから引用させていただきます。
OIDCのUserInfo エンドポイントは認証以外の目的でユーザー情報を利用する際に利用します。例えば、新規登録のためにエンドユーザーのemailや住所を取得したり、ログイン後に表示するユーザー名を取得するといった用途です。
OIDCではUserInfo エンドポイントは仕様として定義されているので、OIDCに準拠するIDプロバイダでは共通になります。UserInfo エンドポイントへのリクエストのHTTPメソッドはGETまたはPOSTのいずれかで行います。Authorizationヘッダーにはベアラートークンとしてアクセストークンをセットします。
レスポンスには必ずsubクレームが含まれます。万一、アクセストークンが入れ替えられていた場合、IDトークンのsubクレームとUserInfoレスポンスのsubクレームの値が異なるため、入れ替えを検知できます。アクセストークンがリダイレクトを介して受け渡される場合、入れ替えられる可能性があるので、リライング・パーティはUserInfoレスポンスのsubクレームとIDトークンのsubクレームの一致を必ず確認しなければなりません。
Cognito用語
ユーザープール
これは公式ドキュメントから引用します。
Amazon Cognito ユーザープールは、ウェブおよびモバイルアプリケーションの認証と認可のためのユーザーディレクトリです。アプリケーションの観点から見ると、Amazon Cognito ユーザープールは OpenID Connect (OIDC) ID プロバイダー (OIDC) です。ユーザープールには、セキュリティ、ID フェデレーション、アプリ統合、ユーザーエクスペリエンスのカスタマイズなどの機能が何層も追加されます。
2025年の10月のBlackbeltでユーザープール機能の全体像が紹介されていたので、そちらも引用します。ユーザー管理用のディレクトリ機能に留まらず外部IdPとの連携やログイン用画面の用意、MFAやパスキーを用いたセキュリティ機能、ユーザーのログを取得する機能など色々なものをひとまとめにしてユーザープールと呼ばれます。
アイデンティティプール(IDプール)
こちらも公式ドキュメントから引用します。
Amazon Cognito アイデンティティプールは、 AWS 認証情報と交換できるフェデレーションアイデンティティのディレクトリです。ID プールは、サインインしているか、まだ識別していないかにかかわらず、アプリケーションのユーザーに一時的な AWS 認証情報を生成します。
2025年の10月のBlackbeltの108ページ目のリード文からも引用します。(複数の切り口の説明を読むと理解が深まるので)
認証されていないユーザーやユーザープール、外部IDプロバイダ(例: Google, SAML, OIDC等)を用いて認証されたユーザーに対してIAMロールの一時的認証情報を発行する
認証されていないユーザーには匿名ユーザーとして匿名アクセスを提供可能です。
Cognitoトークン
IDトークン、アクセストークン、リフレッシュトークン(オプション)をまとめてそう呼びます。
余談(私がしていた勘違い)
蛇足なので読み飛ばしていただいて大丈夫ですが、個人的に一番書きたかった部分かもしれません。私は記事の執筆前に
ユーザープールでは認証(たまに認可)をして、IDプールでは認可をする
と思い込んでいたのですが、実際は
ユーザープールではアプリケーションレベルの認証・認可を行い、IDプールではユーザーにどのIAMロールの一時認証情報を渡すかを決める
でした。驚きましたが、上記引用部分や、AWS公式のBlackbeltを見ると確かにそうであることがわかります。
上のスライドですと、ユーザープール側に「認証・認可機能を提供」と記載がありますが、IDプール側には「認可」の文字はありません。「AWSのAPIを呼び出す機能を追加する」、「一時クレデンシャルを発行するアイデンティティ・ブローカー」とあります。IDプールはIAMロールで既に認可された権限を使うための情報をくれる存在なので、厳密には認可機能を提供しているわけではないのです。認証も認可もユーザープールが担っています。
上記思い込みのせいで数多の解説記事、書籍、Blackbelt、その他諸々何度読んでもどこかしっくりこなかった部分がありました。どうにもうまくいかず、識者に相談したところ「Cognitoの機能に無理やりOAuth2.0とOIDCをマッピングさせようとしているから、理解がねじれちゃったんだね」と言われ衝撃を受けました。こんな感じ↓
以上、蛇足でした。もし同じ思い込みをしている方がいたら上記を抑えておくと幸せになれるかもしれません。
Cognito JWT Deep Diveをやってみた
長い基本説明が完了しました。手を動かして理解したく、試しにAWS公式のWorkshopを触ってみました。AWS公式のWorkshop【Cognito JWT Deep Dive】というものです。必要なリソースはCDKで自動構築され、内容自体も1-2時間で終わる内容なので、ご興味がある方はぜひお手元でお試しください。以下はWorkshopの補足説明というか備忘です。
※リソースは削除済みなので、あえて値にはマスクしていません。
※最後のセッション5: Amazon API Gatewayは省略します。
※何回かWorkshopを実施したため、画像によって微妙に値が異なっています。
Setup
CDKを用いてWorkshop環境を自動作成します。ここでアプリケーション、CognitoのユーザープールやIDプールが作成されます。詳細を知りたい方はCloudFormationのリソースを確認しましょう。
1.Example Tokens
架空のIssuerであるhttps://example.com によって実際に発行されたIDトークンの中身を確認し、aws-jwt-verifyライブラリを使ってUIベースで実際に検証するセッションです。
2.Cognito Pools
以下の流れです。
1.Amplifyで自動作成されたアプリに対して、これまた自動作成されたCognitoのユーザープールとIDプールを紐づけます
2.Hosted UI経由でユーザーt1u1をCognitoのユーザープールで認証します。パスワードは/home/cloudshell-user/jwt-workshop/cdk/util/createUsers.tsに記載されている通り、S3__demoです。リダイレクトURLは自分の場合はhttps://main.d1orwbkavymrgt.amplifyapp.com/tokens?code=2d63ee57-7dc4-434f-b569-f55b1a47fbbf&state=m1km2b1dsxbでした。クエリパラメータcodeの値は認可コードです。この認可コードでアクセストークンとIDトークンを取得します
3.取得したIDトークンをクライアント側で検証(Verify)します。ボタンを押すだけで終わってしまいますが、少し詳しく書くと
➀ OpenID Configuration URL(
https://cognito-idp.リージョン.amazonaws.com/ユーザープールID/.well-known/openid-configurationの形で表示)にユーザープール発行者のメタデータを取りに行きます
➁ メタデータから検証に使う公開鍵の情報を得る。メタデータの中身は例えば以下のようになっていて、OpenID Configuration URLからアクセスできます。
jwks_uriが公開鍵の置き場所です
{"authorization_endpoint":"https://d1orwbkavymrgt.auth.us-east-1.amazoncognito.com/oauth2/authorize","end_session_endpoint":"https://d1orwbkavymrgt.auth.us-east-1.amazoncognito.com/logout","id_token_signing_alg_values_supported":["RS256"],"issuer":"https://cognito-idp.us-east-1.amazonaws.com/us-east-1_u6ICFYxuV","jwks_uri":"https://cognito-idp.us-east-1.amazonaws.com/us-east-1_u6ICFYxuV/.well-known/jwks.json","response_types_supported":["code","token"],"revocation_endpoint":"https://d1orwbkavymrgt.auth.us-east-1.amazoncognito.com/oauth2/revoke","scopes_supported":["openid","email","phone","profile"],"subject_types_supported":["public"],"token_endpoint":"https://d1orwbkavymrgt.auth.us-east-1.amazoncognito.com/oauth2/token","token_endpoint_auth_methods_supported":["client_secret_basic","client_secret_post"],"userinfo_endpoint":"https://d1orwbkavymrgt.auth.us-east-1.amazoncognito.com/oauth2/userInfo"}
➂
jwks_uriにアクセスし、IDトークンのヘッダのkidから利用する公開鍵を選択します。
{"keys":[{"alg":"RS256","e":"AQAB","kid":"UM6vOtEhQAblKh/6q7Yeu+Ca0rv+3TY9x0LS7cGDZ8s=","kty":"RSA","n":"xYzhLxOMGLRT3eQu_Rse0INYEhszp4lDlkL2RrN6dIemq0OTBkpkx4Y3IZ5obRlo_enoqqF22AA6VkrJAQtxiDScKvicwJcJ9UugFqhhW0TIPEH9LX609qmOWuSHCUtoG6AQrblKoV8_EaYix8ohLEJw3VOmG-t6VpMBIqu83UJfYCJCWpF4-eQFrY6ekx3GCUhKEKcS-qqoIT3Y_oKk4FwWCt-YjLprsyOmuUWJ3xUl31MmO5ICNsSKxFLH3QXiU7DmgsdlFtzAGVa01PaTxfANUaXAgmIsrF5tUWZxUtFwRjIWOeeuk020yFByU4VU0bGvxnm8VyFsgmRER0kmSw","use":"sig"},{"alg":"RS256","e":"AQAB","kid":"N50XH9eqSCJrB2e51XuC2htiBRQH9dZVcQR5emBLeKM=","kty":"RSA","n":"zJbE8dIJwQHknzhS7cT5GgDt5wNJ72-ALljnJX82VlyUtfvke5bQf1nC7CaFicfmAFR9d2jHEuXew_u4phVeUBX-wMPSdPg2x5ZMy2y4-sh6WGh9jLBXEWsJI_LMf205cWPI6VF4G5NtHiQaEVk7zTd6zg9aYc4DH0V_g72wuXwSftWlAlvrcKtQTRb3siB5iCCHnBSrA5pYt6fytwohdZ1iqAh9SsbTpSG9VQL83VyXH9PwYqQv4CxNKv1N6sWZz5j7C4xBs4SM6kyuxGDf_3xTb4bD8Bsg5r6bty1KeY4C9uzyd7mw8pYDmJx-_CJt1GZkUyLZ4eFJ2cfwbmeLWw","use":"sig"}]}
➃公開鍵で署名検証します。IDトークンのヘッダとペイロードの値=署名から復号した値であれば、暗号化できるのは発行者だけなのでIDトークンは正しいとみなされ、検証が完了します。
4.Get IAM Credentialsでは、IDプール向けにGetCredentialsForIdentityのAPIをたたき、STSから一時的な認証情報(アクセスキー、シークレットアクセスキー、セッショントークン)を取得します。IDトークン関連の操作は以上です
5.取得したアクセストークンのClaimのscopeを見ると、objects/list aws.cognito.signin.user.admin openid profile objects/readのように複数のスコープが含まれていることが分かります。このうちopenidが OIDCのIDトークン・UserInfo 連携のためのスコープで、objects/listやobjects/readが今回のサンプルアプリ用の権限を表しています。
6.取得したアクセストークンをクライアント側で検証(Verify)します。CognitoのアクセストークンはIDトークンと同じくJWT方式で、細かい流れはIDトークンの検証時と同じ(のはず)です
7.検証が完了したIDトークンとアクセストークンを見比べます。IDトークンはユーザーが誰であるか特定する身分証ですので、token_useの値がidになっており、custom:で始まるカスタム属性が入っています。一方アクセストークンはスコープに定められた範囲内でリソースにアクセス・操作できるものなので、token_useの値がaccessで、scopeが存在します。今回発行したIDトークンとアクセストークンの比較結果は以下の通りです。緑が一致する値、赤が差分です。ちなみに、画像上部分タブのClaimsはPayloadを見やすく整理しているだけです。
8.OpenID Configuration URL(https://cognito-idp.リージョン.amazonaws.com/ユーザープールID/.well-known/openid-configurationの形で表示)にユーザープール発行者のメタデータを取りに行きます。(上の3の➀と同じ)
9.メタデータの中にあるuserinfo_endpointのURLに対してGETリクエストを、アクセストークンをつけて送ります。自分の場合はhttps://d1orwbkavymrgt.auth.us-east-1.amazoncognito.com/oauth2/userInfoでした。メタデータ詳細は以下の通りです。(再掲)
{"authorization_endpoint":"https://d1orwbkavymrgt.auth.us-east-1.amazoncognito.com/oauth2/authorize","end_session_endpoint":"https://d1orwbkavymrgt.auth.us-east-1.amazoncognito.com/logout","id_token_signing_alg_values_supported":["RS256"],"issuer":"https://cognito-idp.us-east-1.amazonaws.com/us-east-1_u6ICFYxuV","jwks_uri":"https://cognito-idp.us-east-1.amazonaws.com/us-east-1_u6ICFYxuV/.well-known/jwks.json","response_types_supported":["code","token"],"revocation_endpoint":"https://d1orwbkavymrgt.auth.us-east-1.amazoncognito.com/oauth2/revoke","scopes_supported":["openid","email","phone","profile"],"subject_types_supported":["public"],"token_endpoint":"https://d1orwbkavymrgt.auth.us-east-1.amazoncognito.com/oauth2/token","token_endpoint_auth_methods_supported":["client_secret_basic","client_secret_post"],"userinfo_endpoint":"https://d1orwbkavymrgt.auth.us-east-1.amazoncognito.com/oauth2/userInfo"}
10.レスポンスがエンドポイントから返ってきます。返ってきた値のsubとIDトークンのsubを比べます。一致しているので、改ざんの心配はなさそうです。
11.Tokens(Part2)以降はほぼ同じことの繰り返しです。ただし、以下が異なります。
-
HostedUIではなく、InitiateAuth APIで別ユーザーt2u1を認証します。画面で触ったUIとアプリの裏で動いているコード/home/cloudshell-user/jwt-workshop/src/features/cognito/cognitoapis.tsの対応関係が確認できます。コードの詳細は省略します -
t2u1のアクセストークンのscopeにopenidが含まれないため、UserInfoエンドポイントからユーザー情報が返ってきません
3.Temporary Credentials
直前に発行した2つのAWS一時認証情報を確認するセッションです。t1u1とt2u1で同じIAMロールを利用しているため、IAM User ID(※)とIAM Role ARNについては同じ値ですが、Cognitoで管理しているユーザー固有のCognito Identity ID、そしてユーザーごとに払い出されたAccess Key IDは異なります。
※表記がややこしいですが、これはAssumeRoleされたロールセッションの UserId(IAMロール固有のRoleIdを基に算出された値)です。固有のIAMユーザーが存在しているわけではないです。
AWSリソースにアクセスする一時認証情報を発行するのはIDプールなので、IDプールも確認してみましょう。認証済みユーザーに割り当てるIAMロール、認証されていないゲストアクセスに割り当てるIAMロールが設定されています。
認証済みユーザー用のIAMロールのポリシーは以下の通りでした。デモ用に作成したバケット内のファイルへのアクセスをセッションタグを用いて、ユーザーごとに制御しています。
{
"Version": "2012-10-17",
"Statement": [
{
"Condition": {
"StringEquals": {
"s3:ExistingObjectTag/tenant": [
"${aws:PrincipalTag/tenant_id}"
]
},
"StringEqualsIfExists": {
"s3:ExistingObjectTag/owner": [
"${aws:PrincipalTag/user_id}"
]
}
},
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::jwtworkshopstack-saasbucket419c11fe-qumbyykaqytu/${aws:PrincipalTag/tenant_id}/*",
"Effect": "Allow"
},
{
"Condition": {
"StringLike": {
"s3:prefix": [
"${aws:PrincipalTag/tenant_id}/*"
]
}
},
"Action": "s3:ListBucket",
"Resource": "arn:aws:s3:::jwtworkshopstack-saasbucket419c11fe-qumbyykaqytu",
"Effect": "Allow"
}
]
}
IDプールのマッピング情報(IDトークンのどのclaimを、タグキーとして割り当てるか)は以下の通りです。

ユーザーt1u1のIDトークンのcustom:tenant_idはT1、custom:user_idはU1なので、PrincipalTag/tenant_idにはT1、PrincipalTag/user_idにはU1が入ります。なので、今回であればt1u1は対象S3バケットのT1配下のオブジェクトにアクセス可能です。
ちなみに認証済みでないユーザー向けのIAMロールはポリシーがついておらず、何もできない状態でした。
4.Amazon S3
発行した2つのAWS一時認証情報をつかって、実際にS3バケットにアクセスできるかどうかを試しています。検証にあたっては、既存環境に影響を与えないよう、Dockerで一時的なサンドボックス環境を用意して検証しています。
Workshopの流れまとめ
Workshop掲載の画像にセッションのどこにあたるか対応関係を記載しました。
最後に
Cognitoや認証認可周りは本当に難しいです。ちゃんと理解して言語化できる方、本当に尊敬します。今回何とか自分なりにまとめることができたものの、数日たったらまた記憶が飛びそうな予感です。そのためのブログ 案件でガッツリ触ることになったらまた戻ってこようと思います。色々盛り込もうとしてかなり長くなってしまいましたが、ここまでお読みいただきありがとうございました。明日以降のアドカレ記事もお見逃しなく!
参考
AWS公式
全体的にある程度知識がある方向けです。初学者には少し難しいかもしれません。
Black Belt Online Seminar
Workshop Studio
今回備忘を残した無料のAWS公式Workshopです。知識がガタガタな状態で挑むと、何をしているかよくわからなくなるのである程度本記事の基本内容を押さえる、あるいは下記やお手元の教材で勉強してから挑みましょう。いい復習になると思います。
Udemy
個人的には一番初心者向けでわかりやすかったので、何をやろうか迷ったら断然これがおすすめです。中々ボリューミーですが、解説だけでなく丁寧なハンズオンもついているので理解が深まります。
書籍
下記2つは技術書展の名著です。OAuth2.0、OIDCを勉強する中でお世話になったエンジニアは多いでしょう。
下記はまだ読んでいませんが、識者がお勧めの書籍とのことで併せて紹介します。
おまけ
まとめましたが、流れとして今回ほぼ出番がなかった用語説明です。消すのももったいない気がしたのでここに記します。
Identity Provider(IdP)とは
認証情報を管理しており、ユーザー認証後にその結果を他のシステムに渡す役割を担います。CognitoのユーザープールがIdPにあたりますが、もしユーザープールが他の外部IdP(Google, Apple, Facebookなど)と連携している場合、その連携先がIdPの役割を担います。外部IdP連携時のユーザープールはブローカーとして働きます。
Single Sign On(SSO)
一度の認証で複数サービスへログインできる仕組みです。AWS IAM Identity CenterやGoogle Workspaceで実現可能です。
Security Assertion Markup Language(SAML)
標準化団体であるOASIS(Organization for the Advancement of Structured Information Standards)のページから引用します。
Defines the syntax and semantics for XML-encoded assertions about
authentication, attributes, and authorization, and for the protocols that convey this information.
(自動翻訳:認証・属性・認可に関する内容をXMLで表した記述の書式と意味、そしてその情報をやり取りするためのプロトコルを定める)
SSOを実現するための手法の1つで、IdPとサービスプロバイダー間で認証情報を連携させる際に用います。













