ネクストモードブログ

【Okta】取り込み済み AgentCore エージェントの認可土台として ID-JAG トークン交換を検証してみた【生成AIセキュリティ対策シリーズ】|Nextmode Blog

作成者: テクニカルサポート|2026/07/27 22:59

はじめに

こんにちは、ネクストモード株式会社テクニカルサポート担当です。

前回の記事では、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つです。

  1. 「誰の代理か」を Org Authorization Server で確定する(id_token → ID-JAG)
  2. 「何をしてよいか」を 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 で自分だと証明し、交換リクエストを投げる

利用順を箇条書きにすると次のとおりです。

  1. ユーザーが OIDC Web App にログインし、id_token を得る
  2. エージェントが Org Authorization Server で id_token → ID-JAG を行う(Delegations が効く)
  3. エージェントが Custom Authorization Server で ID-JAG → access_token を行う(Access Policy が効く)
  4. 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_tokenaccess_token は役割が違うためです。通常の OAuth / OIDC では、まずユーザーがアプリにログインし、アプリは「このユーザーは誰か」を示す id_token を受け取ります。ただし、id_token は基本的に「ユーザーがこのアプリにサインインしたこと」を示すためのトークンであり、そのまま別のリソースや MCP サーバーを呼び出すための access_token ではありません。

まず、比較対象として通常の OIDC ログインだけを見ると、流れは次のようになります。

 

ここで得られる id_token は、あくまで「ユーザーが OIDC Web App にログインしたこと」を示すものです。つまり、この時点では「ユーザーが誰か」は分かりますが、「エージェントがそのユーザーの代理として、どのリソースに何をしてよいか」までは決まっていません。

そこで Cross App Access / ID-JAG では、標準的な OIDC ログインのあとに、次の流れを追加します。

  1. ユーザーの本人性と代理関係を確認する段階 エージェントは Org Authorization Server に対して、ユーザーの id_token を提示します。Okta は「その id_token がこのエージェント向けに発行されたものか」「ユーザーが有効か」「このエージェントがユーザーの代理として動いてよいか」を確認します。問題なければ、Okta は ID-JAG(Identity Assertion JWT Authorization Grant)を返します。
  2. リソース用の 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 を設定します。

設定の流れは以下のとおりです。

  1. AI エージェントを Active 化し、トークン交換で使う資格情報を控えます。
  2. Custom Authorization Server を作成し、検証用スコープと JWT Bearer Grant のポリシーを設定します。
  3. id_token を取得するための OIDC Web App を作成し、Delegations でエージェントと紐づけます。
  4. Resource connections に Custom Authorization Server を追加し、エージェントから利用できるリソースとして許可します。

AI エージェントを Active 化し、資格情報を用意する

  1. Okta Admin Console > Directory > AI Agents で対象エージェントのオプションメニューから Activate をします。
  2. 対象エージェントをクリックし、左側メニューから Credentials を開き、Add public key をクリックします。
  3. Generate new key をクリックします。
  4. 表示される Private key の値を控えます。
  5. 追加が完了すると以下のように表示されます。
    • AI agent ID:環境変数 AGENT_CLIENT_ID
    • KEY ID:環境変数 AGENT_KEY_ID
    • Private key(JSON):環境変数 AGENT_PRIVATE_KEY_JWK

Custom Authorization Server を用意する

次に、ID-JAG を提示して scoped access_token を発行してもらうための Custom Authorization Server を用意します。

今回の検証では、新規 Custom Authorization Server を作成しました。例として、Name は agentcore-demo-as としています。

  1. Okta Admin Console > Security > API > Authorization Servers > Add Authorization Server から Custom Authorization Server を作成します。
  2. Name と Audience を以下のように設定します。
  3. Scopes タブから以下のようにカスタムスコープ xaa:read を追加します。
  4. Access Policies を作成します(例: AgentCore Demo Token Exchange)。
  5. 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

このときの ausXXXXXXXXOKTA_CUSTOM_AS_ID です。

OIDC Web App を作り、Delegations で繋ぐ

続いて、ユーザーの id_token を取得するための OIDC Web App を作成します。エージェントをインポートしただけでは、ユーザーの id_token は取得できないため、ログイン用の Web アプリを別途用意します。

  1. Okta Admin Console > Applications > Create App Integration から OIDC と Web Application を選択し、Web アプリを作成します。
  2. 今回は次のような設定にしました。
    • Name:AgentCore Demo User Sign-In
    • Grant:Authorization Code
    • Sign-in redirect URI:http://localhost:8765/callback(ローカル検証用)
  3. Assignments で自分のユーザーを割り当てます。
  4. Directory > AI Agents > 対象エージェント > Delegations > User sign-onAdd caller をクリックします。
  5. Application に作成した OIDC アプリを選択し、Confirm authorization server という設定項目では、Org Authorization Server を選びます。

Resource connections に Custom Authorization Server を載せる

続いて、エージェントの Resource connections に、作成した Custom Authorization Server を追加します。ここで許可したリソースに対してだけ、エージェントがトークン交換を進められることを確認します。

  1. Okta Admin Console > Directory > AI Agents で対象エージェントを開き、Resource connections を表示します。
  2. Resource type として Authorization server を選択します。
  3. 対象の Authorization Server として agentcore-demo-as を選択し、Scopes で xaa:read を選択し、Add をクリックします。
  4. Resource connections に Custom Authorization Server が ACTIVE ステータスで追加されていることを確認します。

 

トークン交換を検証する

ここまで設定できたら、ローカル CLI からトークン交換を実行します。検証では AgentCore Runtime にはまだ組み込まず、トークン交換だけのローカル CLI を使いました。まずは Okta 側の設定とトークン交換の流れが正しいことを、最小構成で確認するためです。

確認する内容は次の2つです。

  1. Resource connections で Custom Authorization Server への利用を許可しているときに、access_token が発行されること
  2. 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 と有効期限などが返ります。

以下は成功時のログです。

 

処理の概要

ここではコードの詳細には踏み込まず、検証用スクリプトの中で行われるトークン交換の流れだけを整理します。

  1. エージェントとしてリクエストするための client_assertion を作成します。
    • agent の private JWK を使って private_key_jwt を署名します。
  2. 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 を取得
  3. Custom Authorization Server に ID-JAG を提示し、access_token を発行してもらいます。
    • endpoint:/oauth2/{customAsId}/v1/token
    • grant_type:jwt-bearer
    • assertion:ID-JAG
    • 結果:scoped access_token を取得

要するに、最初に Org Authorization Server で「誰の代理か」を確認し、その結果として発行された ID-JAG を使って、Custom Authorization Server から「何をしてよいか」を表す access_token を取得する流れです。

許可/拒否の切り替えを確認する

  1. Resource connections で指定している Custom Authorization Server を Deactivate にします。
  2. 再度スクリプトを実行すると以下のようにエラーとなります。
  3. 確認後、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 接続をどこまで安全に制御できるかにフォーカスします。

 

参考リンク