【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つです。

  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 に対するアクセスを許可するかどうかを判定する位置づけです。

ローカル CLI、Org Authorization Server、Custom Authorization Server、Resource connections を使ったトークン交換の検証構成を示す図

 

登場人物の役割

検証構成の図に出てきた各要素の役割を整理しておきます。

対象 役割
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 ログインだけを見ると、流れは次のようになります。

OIDC Web App と Org Authorization Server の間で通常の OIDC ログインにより id_token を取得する流れを示す図

 

ここで得られる 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 を発行します。

この追加部分だけをクローズアップすると、次の流れになります。

エージェントが id_token から ID-JAG を取得し、Custom Authorization Server で 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 をします。
    Okta Admin Console の AI Agents 一覧で対象エージェントを Activate する操作を示す画面
  2. 対象エージェントをクリックし、左側メニューから Credentials を開き、Add public key をクリックします。
    Okta AI Agent の Credentials 画面で Add public key をクリックして鍵追加を開始する画面
  3. Generate new key をクリックします。
    Okta AI Agent の鍵追加画面で Generate new key をクリックして新しい鍵を生成する操作を示す画面
  4. 表示される Private key の値を控えます。
    Okta AI Agent の鍵生成後に Private key が表示され、検証用に控える必要があることを示す画面
  5. 追加が完了すると以下のように表示されます。
    Okta AI Agent の Credentials 画面で公開鍵の追加が完了し、KEY ID などの資格情報を確認できる状態を示す画面 今回の検証では以下の値をエージェントの環境変数として利用します
    • 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 を作成します。
    Okta Admin Console の Authorization Servers 画面で Custom Authorization Server を追加する操作を示す画面
  2. Name と Audience を以下のように設定します。
    Okta の Custom Authorization Server 作成画面で Name と Audience を設定する画面
  3. Scopes タブから以下のようにカスタムスコープ xaa:read を追加します。
    Okta の Custom Authorization Server の Scopes タブで検証用カスタムスコープ xaa:read を追加する画面
  4. Access Policies を作成します(例: AgentCore Demo Token Exchange)。
    Okta の Custom Authorization Server で Access Policy を作成し、トークン交換用のポリシーを準備する画面
  5. Rule で Grant type = JWT Bearer、Scopes = xaa:read を許可します。
    Okta の Access Policy 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 アプリを作成します。
    Okta の Create App Integration 画面で OIDC と Web Application を選択し、ログイン用アプリを作成する画面
  2. 今回は次のような設定にしました。
    Okta の OIDC Web App 設定画面でローカル検証用のアプリ名やリダイレクト URI を設定する画面
    • 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 をクリックします。
    Okta AI Agent の Delegations の User sign-on 画面で Add caller をクリックして OIDC アプリ連携を追加する画面
  5. Application に作成した OIDC アプリを選択し、Confirm authorization server という設定項目では、Org Authorization Server を選びます。Okta AI Agent の Delegations 設定で 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 を表示します。
    Okta AI Agent の Resource connections 画面でエージェントに接続するリソースを追加する準備を示す画面
  2. Resource type として Authorization server を選択します。
    Okta AI Agent の Resource connections で Resource type に Authorization Server を選択する画面
  3. 対象の Authorization Server として agentcore-demo-as を選択し、Scopes で xaa:read を選択し、Add をクリックします。
    Okta AI Agent の Resource connections で対象の Custom Authorization Server とスコープ xaa:read を選択して追加する画面
  4. Resource connections に Custom Authorization Server が ACTIVE ステータスで追加されていることを確認します。
    Okta AI Agent の 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 と有効期限などが返ります。

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

ローカル検証用スクリプトの実行ログで ID-JAG と 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 にします。
    Okta AI Agent の Resource connections で Custom Authorization Server を Deactivate する操作を示す画面
  2. 再度スクリプトを実行すると以下のようにエラーとなります。
    Resource connections の許可を外した後に検証用スクリプトを再実行し、トークン交換が失敗したログ画面
  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 接続をどこまで安全に制御できるかにフォーカスします。

 

参考リンク