【Okta】取り込み済み AgentCore エージェントの認可土台として ID-JAG トークン交換を検証してみた【生成AIセキュリティ対策シリーズ】
はじめに
こんにちは、ネクストモード株式会社テクニカルサポート担当です。
前回の記事では、Amazon Bedrock AgentCore Runtime 上の自作エージェントを Okta for AI Agents に取り込み、「Where are my agents?(エージェントはどこにいるか)」までをご紹介しました。あわせて、次回以降は Slack や Notion などの MCP 連携と、接続・認可の制御(許可/拒否)を検証していく、と予告していました。
今回はそのうち、認可の土台にあたる部分を先に確認します。前回取り込んだ AgentCore エージェントを題材にしますが、今回は AgentCore Runtime にはまだ組み込まず、ローカル CLI からトークン交換だけを実行します。取り込んだエージェントを Active にし、ユーザーの id_token から ID-JAG を介して scoped access_token を得るトークン交換フローと、Resource connections による許可/拒否までを確認します。AgentCore Runtime や Slack MCP など実ツールへの適用は次回以降に行う予定です。
前回の記事
今回の検証でのポイントは次の2つです。
- 「誰の代理か」を Org Authorization Server で確定する(
id_token→ ID-JAG) - 「何をしてよいか」を Custom Authorization Server で狭く発行する(ID-JAG →
access_token)
画面操作とトークンの流れを中心に見ていきます。実装の細部は、必要に応じて公式ドキュメントを参照してください。
※本記事は執筆時点の情報に基づきます。Early Access や仕様変更により、画面や挙動が変わる場合があります。最新情報は公式ドキュメントをご確認ください。
今回確認すること
- インポート済み AI エージェントを使ったトークン交換に必要な設定要素(Active 化、資格情報、Delegations、Custom Authorization Server、Resource connections)の役割
id_token→ ID-JAG →access_tokenの流れ- Resource connections の許可が有効なら交換成功、無効なら失敗すること
今回はやらないこと:
- Slack / Notion などの MCP を Okta 経由に載せること
- AgentCore Runtime へのトークン交換組み込み
検証構成
今回の検証では、AgentCore Runtime に組み込む前段階として、ローカル CLI からトークン交換だけを実行します。ユーザーは OIDC Web App にログインして id_token を取得し、その id_token をエージェント資格情報を持つローカル CLI が受け取ります。
その後、ローカル CLI はまず Org Authorization Server に id_token を提示して ID-JAG を取得します。続いて、その ID-JAG を Custom Authorization Server に提示し、検証用スコープ xaa:read を持つ scoped access_token を取得します。Resource connections は、この Custom Authorization Server に対するアクセスを許可するかどうかを判定する位置づけです。
登場人物の役割
検証構成の図に出てきた各要素の役割を整理しておきます。
| 対象 | 役割 |
|---|---|
| OIDC Web App | 人がログインし、id_token を発行する |
| Org Authorization Server | 「誰の代理か」を確定(id_token → ID-JAG)。Delegations の Confirm authorization server 設定項目では、ここを指定する |
| Custom Authorization Server | 「何をしてよいか」を狭く発行(ID-JAG → access_token)。今回の検証で Resource connections の許可対象にする認可サーバー |
| AI Agent(Credentials) | private_key_jwt で自分だと証明し、交換リクエストを投げる |
利用順を箇条書きにすると次のとおりです。
- ユーザーが OIDC Web App にログインし、
id_tokenを得る - エージェントが Org Authorization Server で
id_token→ ID-JAG を行う(Delegations が効く) - エージェントが Custom Authorization Server で ID-JAG →
access_tokenを行う(Access Policy が効く) - Resource connections で Custom Authorization Server への利用が許可されていれば成功し、許可を外すと失敗する
ここでやや分かりづらいのが、Authorization Server が複数出てくる点です。今回の記事では、次の2つを分けて考えると整理しやすくなります。
- Org Authorization Server Okta 組織標準の Authorization Server です。今回の流れでは、ユーザーの
id_tokenを確認し、エージェントがそのユーザーの代理として動いてよいかを確認したうえで ID-JAG を発行する役割を持ちます。 - Custom Authorization Server 検証用に作成した Authorization Server です。ID-JAG を受け取り、Access Policy や Resource connections の許可設定を見たうえで、
xaa:readのような必要なスコープだけを持つaccess_tokenを発行します。
なぜ ID-JAG を介して access_token を取得するのか
ここで少しだけ、ID-JAG を介して access_token を取得する意味を補足しておきます。
先にポイントを言うと、id_token と access_token は役割が違うためです。通常の OAuth / OIDC では、まずユーザーがアプリにログインし、アプリは「このユーザーは誰か」を示す id_token を受け取ります。ただし、id_token は基本的に「ユーザーがこのアプリにサインインしたこと」を示すためのトークンであり、そのまま別のリソースや MCP サーバーを呼び出すための access_token ではありません。
まず、比較対象として通常の OIDC ログインだけを見ると、流れは次のようになります。
ここで得られる id_token は、あくまで「ユーザーが OIDC Web App にログインしたこと」を示すものです。つまり、この時点では「ユーザーが誰か」は分かりますが、「エージェントがそのユーザーの代理として、どのリソースに何をしてよいか」までは決まっていません。
そこで Cross App Access / ID-JAG では、標準的な OIDC ログインのあとに、次の流れを追加します。
- ユーザーの本人性と代理関係を確認する段階 エージェントは Org Authorization Server に対して、ユーザーの
id_tokenを提示します。Okta は「そのid_tokenがこのエージェント向けに発行されたものか」「ユーザーが有効か」「このエージェントがユーザーの代理として動いてよいか」を確認します。問題なければ、Okta は ID-JAG(Identity Assertion JWT Authorization Grant)を返します。 - リソース用の access_token を発行してもらう段階 エージェントは取得した ID-JAG を Custom Authorization Server に提示します。Custom Authorization Server は ID-JAG の署名や
audience、有効期限などを検証し、Access Policy や Resource connections の許可設定を見たうえで、必要なスコープだけを持つaccess_tokenを発行します。
この追加部分だけをクローズアップすると、次の流れになります。
通常の OIDC ログイン図と並べて見ると、id_token をそのまま API 用トークンとして使うのではなく、Org Authorization Server が id_token を検証して ID-JAG を発行し、その ID-JAG を Custom Authorization Server に提示して必要なスコープだけを持つ access_token を発行してもらう流れだと分かります。
つまり、ID-JAG は最終的な API 呼び出し用トークンではなく、「このエージェントは、このユーザーの代理として、このリソースに向かおうとしている」ことを示す Identity Assertion JWTです。最終的に API や MCP サーバーへ渡すのは、ID-JAG をもとに発行される access_token です。
今回の検証では、一般的な SaaS 側の Authorization Server の代わりに、Okta の Custom Authorization Server をリソース側の認可サーバーとして使っています。そのため、登場人物は少し違いますが、考え方は同じです。Token Exchange で「誰の代理か」を確定し、JWT Bearer Grant で「何をしてよいか」を狭く発行する、という分離がポイントです。
ID-JAG や Cross App Access の背景は、過去記事の ID-JAG の設計思想から読み解く Cross App Access の背景と技術の裏側 でも整理しています。また、Okta Developer Blog の Cross-App Access sequence diagram でも、標準的な OIDC ログインのあとに追加のトークン交換を行う流れが紹介されています。
設定手順
ここからが今回の検証の本編です。まずは Okta Admin Console 上で、トークン交換に必要なエージェント、Authorization Server、Delegations、Resource connections を設定します。
設定の流れは以下のとおりです。
- AI エージェントを Active 化し、トークン交換で使う資格情報を控えます。
- Custom Authorization Server を作成し、検証用スコープと JWT Bearer Grant のポリシーを設定します。
id_tokenを取得するための OIDC Web App を作成し、Delegations でエージェントと紐づけます。- Resource connections に Custom Authorization Server を追加し、エージェントから利用できるリソースとして許可します。
AI エージェントを Active 化し、資格情報を用意する
- Okta Admin Console > Directory > AI Agents で対象エージェントのオプションメニューから Activate をします。

- 対象エージェントをクリックし、左側メニューから Credentials を開き、Add public key をクリックします。

- Generate new key をクリックします。

- 表示される Private key の値を控えます。

- 追加が完了すると以下のように表示されます。
- AI agent ID:環境変数
AGENT_CLIENT_ID - KEY ID:環境変数
AGENT_KEY_ID - Private key(JSON):環境変数
AGENT_PRIVATE_KEY_JWK
- AI agent ID:環境変数
Custom Authorization Server を用意する
次に、ID-JAG を提示して scoped access_token を発行してもらうための Custom Authorization Server を用意します。
今回の検証では、新規 Custom Authorization Server を作成しました。例として、Name は agentcore-demo-as としています。
- Okta Admin Console > Security > API > Authorization Servers > Add Authorization Server から Custom Authorization Server を作成します。

- Name と Audience を以下のように設定します。

- Scopes タブから以下のようにカスタムスコープ
xaa:readを追加します。
- Access Policies を作成します(例:
AgentCore Demo Token Exchange)。
- Rule で Grant type = JWT Bearer、Scopes =
xaa:readを許可します。
以降の手順で Resource connections に Custom Authorization Server を追加したり、ローカル CLI からトークン交換を実行したりする際に、この Custom Authorization Server の Issuer と ID を使います。Issuer の例は次のとおりです。
https://<your-okta-domain>/oauth2/ausXXXXXXXX
このときの ausXXXXXXXX が OKTA_CUSTOM_AS_ID です。
OIDC Web App を作り、Delegations で繋ぐ
続いて、ユーザーの id_token を取得するための OIDC Web App を作成します。エージェントをインポートしただけでは、ユーザーの id_token は取得できないため、ログイン用の Web アプリを別途用意します。
- Okta Admin Console > Applications > Create App Integration から OIDC と Web Application を選択し、Web アプリを作成します。

- 今回は次のような設定にしました。
- Name:
AgentCore Demo User Sign-In - Grant:Authorization Code
- Sign-in redirect URI:
http://localhost:8765/callback(ローカル検証用)
- Name:
- Assignments で自分のユーザーを割り当てます。
- Directory > AI Agents > 対象エージェント > Delegations > User sign-on の Add caller をクリックします。

- Application に作成した OIDC アプリを選択し、Confirm authorization server という設定項目では、Org Authorization Server を選びます。

Resource connections に Custom Authorization Server を載せる
続いて、エージェントの Resource connections に、作成した Custom Authorization Server を追加します。ここで許可したリソースに対してだけ、エージェントがトークン交換を進められることを確認します。
- Okta Admin Console > Directory > AI Agents で対象エージェントを開き、Resource connections を表示します。

- Resource type として Authorization server を選択します。

- 対象の Authorization Server として
agentcore-demo-asを選択し、Scopes でxaa:readを選択し、Add をクリックします。
- Resource connections に Custom Authorization Server が ACTIVE ステータスで追加されていることを確認します。

トークン交換を検証する
ここまで設定できたら、ローカル CLI からトークン交換を実行します。検証では AgentCore Runtime にはまだ組み込まず、トークン交換だけのローカル CLI を使いました。まずは Okta 側の設定とトークン交換の流れが正しいことを、最小構成で確認するためです。
確認する内容は次の2つです。
- Resource connections で Custom Authorization Server への利用を許可しているときに、
access_tokenが発行されること - Resource connections の許可を外すと、トークン交換が失敗すること
ローカルで id_token を取り、交換する
使った設定項目
| 変数 | 用途 |
|---|---|
OKTA_DOMAIN |
Org ドメイン(https:// なし) |
OKTA_CUSTOM_AS_ID |
Custom Authorization Server の ID(aus...) |
OKTA_SCOPE |
xaa:read |
AGENT_CLIENT_ID / AGENT_KEY_ID / AGENT_PRIVATE_KEY_JWK |
エージェント資格情報 |
OKTA_OIDC_CLIENT_ID / OKTA_OIDC_CLIENT_SECRET |
OIDC アプリ(id_token 取得用) |
実行
今回は、id_token の取得と ID-JAG を介したトークン交換を確認するために、ローカルで実行する検証用スクリプトを用意しました。スクリプトでは、OIDC ログインで取得した id_token を環境変数として渡し、エージェント資格情報を使ってトークン交換フローを実行します。成功時は access_token と有効期限などが返ります。
以下は成功時のログです。

処理の概要
ここではコードの詳細には踏み込まず、検証用スクリプトの中で行われるトークン交換の流れだけを整理します。
- エージェントとしてリクエストするための
client_assertionを作成します。- agent の private JWK を使って
private_key_jwtを署名します。
- agent の private JWK を使って
- Org Authorization Server にユーザーの
id_tokenを提示し、ID-JAG を発行してもらいます。- endpoint:
/oauth2/v1/token - grant_type:
token-exchange - subject_token:ユーザーの
id_token - requested_token_type:
id-jag - audience:Custom Authorization Server の issuer URL
- 結果:ID-JAG を取得
- endpoint:
- Custom Authorization Server に ID-JAG を提示し、
access_tokenを発行してもらいます。- endpoint:
/oauth2/{customAsId}/v1/token - grant_type:
jwt-bearer - assertion:ID-JAG
- 結果:scoped
access_tokenを取得
- endpoint:
要するに、最初に Org Authorization Server で「誰の代理か」を確認し、その結果として発行された ID-JAG を使って、Custom Authorization Server から「何をしてよいか」を表す access_token を取得する流れです。
許可/拒否の切り替えを確認する
- Resource connections で指定している Custom Authorization Server を Deactivate にします。

- 再度スクリプトを実行すると以下のようにエラーとなります。

- 確認後、Resource connections で指定している Custom Authorization Server を再度 Activate します。
これで「Resource connections で許可されたリソースに対してだけトークン交換が通る」ことを観測できました。
はまりどころまとめ
今回の検証で実際にはまったのは、Delegations の Confirm authorization server という設定項目でした。
この設定項目では、「OIDC Web App から取得した id_token をどの Authorization Server で確認するか」を選びます。今回の構成では、id_token を確認し、「このエージェントがユーザーの代理として動いてよいか」を判断する Org Authorization Server を選択します。
最初はここに Custom Authorization Server(agentcore-demo-as)を選んでしまい、トークン交換時に次のエラーになりました。
'subject_token' is invalid: the client application is not registered for delegation to this agent.
Custom Authorization Server は、Org Authorization Server から発行された ID-JAG を受け取り、scoped access_token を発行するために使います。Delegations の Confirm authorization server で選ぶ対象ではないため、役割を分けて理解しておくと設定時に迷いにくくなります。
おわりに
今回は、前回インポートした AgentCore エージェントを題材にしつつ、AgentCore Runtime にはまだ組み込まず、ローカル CLI から Okta for AI Agents のトークン交換フローと Resource connections による許可/拒否を確認しました。
今回の検証で重要だったのは、トークン交換そのものだけでなく、エージェントがユーザーの代理として動くときに、Okta 側でどこまで制御できるかを確認できた点です。
id_token をそのまま使うのではなく、Org Authorization Server で ID-JAG を発行し、Custom Authorization Server で scoped access_token に落とし込むことで、「誰の代理か」と「何をしてよいか」を分けて扱えることが分かりました。さらに、Resource connections の許可が有効な場合はトークン交換が成功し、許可を外すと失敗することも確認できました。
今回の検証は、AgentCore Runtime や Slack MCP などの実リソースへ接続する前段階です。ユーザー代理のトークン取得と、Resource connections による許可制御の土台を確認できたことで、次の検証では AgentCore Runtime、MCP、Okta for AI Agents を組み合わせ、実際の MCP 接続をどこまで安全に制御できるかにフォーカスします。
参考リンク
- 【Okta for AI Agents】Amazon Bedrock AgentCore のエージェントをインポートしてみた
- 【Okta】Okta for AI Agents とは?シャドー AI 時代に備えるエージェント管理の全体像【生成AIセキュリティ対策シリーズ】
- 【Okta】Cross App Access検証レポート AIエージェント連携の可能性と見通し【生成AIセキュリティ対策シリーズ】
- 【Okta】ID-JAGの設計思想から読み解くCross App Accessの背景と技術の裏側
- Integrate Your Enterprise AI Tools with Cross-App Access
- Secure an Amazon Bedrock AgentCore agent
- Connect AI agents to resources
- Add MCP servers
- Secure an imported Amazon Bedrock AgentCore agent


