ブログ

エージェントに渡す鍵の形——短期・スコープ・上限の三点セット

AWS Japan の認可疲れ論、Retty の $OPENAI_API_KEY 廃止、Gemini の無制限キー拒否——同じ週の三つの話は同じ一点を指す。権限はダイアログではなく鍵の形に。600 秒・300 秒・3600 秒から設計と請求書を整理します。

Leo Kaka約 17 分
Retty Tech Blog の図:AWS STS → アプリケーションサーバー → OpenAI の短期トークン交換フロー(五つのステップ)

エージェントに権限を渡す場所は、実行時の確認ダイアログではなく、鍵そのものの形です。 今週、別々に見える三つの話がその一点に収束しました。AWS Japan の icoxfog417 氏による「AI エージェントの『認可疲れ』に効く処方箋」、 Retty の pikatenor 氏による「NO MORE $OPENAI_API_KEY」、 そして Gemini API が無制限 API キーのリクエストを拒否するという方針。 三つを一枚の表に並べると、残る結論はこれ一つです。鍵の形は三つの要素で決まります—— 短期(長期キーを配らず、数分のトークンに交換する)、スコープ(エンドポイント・操作・モデルを 鍵の中に書く)、上限(鍵ごとの予算とレート)。言い換えると、有効期限・権限範囲・予算上限が キーの属性として最初から付いている状態です。以下、三つの一次情報を順に読み、最後に その設計の請求書を書きます。

今週の三つの出来事

出来事何が変わったか一次情報
AWS Japan が「認可疲れ」の処方箋を公開エージェントの認可要求を、実行時のダイアログから一度の認証+トークン交換(RFC 8693)へZenn 記事(2026-08-16)
Retty が本番アプリから $OPENAI_API_KEY を撤去ECS タスクロール(IAM ロール)を STS が署名した JWT で証明し、OpenAI の短期トークンに交換Retty Tech Blog(2026-08-20)
Gemini API が無制限キーを拒否無制限 standard キーを拒否(開始日 6/19 はフォーラム告知)、2026 年 9 月には standard キー全体を拒否し auth キーへ移行Google 公式ドキュメント公式フォーラム告知

三つとも主語が違います。一つ目はエージェント基盤を作る側、二つ目はエージェントを使う アプリを作る側、三つ目はモデルを提供する側です。立場の異なる三者が同じ週に同じ方向へ 動いたことに意味があります。なお、同じ週の Zenn には ito 氏の「AI エージェントはなぜテストを握り潰すのか」もあり、 エージェントは「完了」までの最短経路を選ぶ、と整理されています。権限の境界をプロンプトの お願いではなく環境の側に置くべき理由は、この一文で足ります。

認可疲れの正体——要求が飛ぶのは実行時

AWS Japan の記事が整理する流れは三段階です。(1) ユーザーが社内 IdP(ID プロバイダ。Google や Entra ID のような認証基盤)で認証し、エージェントが id_token を受け取る。(2) エージェントが ツール(MCP サーバー経由の外部サービス)ごとに認可をユーザーへ求める。 (3) 認可済みのツールを代理実行する。問題は (2) の発生タイミングで、認可要求はツールを 実行しようとした瞬間にしか飛びません。「仕事を任せたと思ったら認可待ちで止まっていた」 という記事の表現は、エージェントを運用したことがある人なら身に覚えがあるはずです。

記事の処方箋は、ダイアログを減らすことではなく、(2) を (1) の結果から機械的に導くことです。 IdP が発行した id_token(「誰が認証したか」の証明)を外部サービスの認可サーバーに提示し、 そのサービス宛の access_token(「何をしてよいか」の許可)に交換する。RFC 8693 のトークン交換、その中でも「ユーザーの代理として」の 用法を OBO(On-Behalf-Of)と呼びます。交換後のトークンには、元の id_token になかった 二つのクレームが入ります。

クレーム / 応答値意味
scopeorder:read order:writeこのトークンで許される操作の範囲
act.subエージェントの client_id誰が代理で取得したか
expires_in600有効期間は 10 分(記事の実装例)

つまり、権限の範囲と代理実行者のアイデンティティが、鍵の中に書かれた状態でエージェントに渡ります。 スコープが足りない操作は、ダイアログではなく 403 で止まります。

デモ画面:受注一覧は成功、発注一覧は「スコープ不足で拒否されました(purchase_order:read が必要)— HTTP 403」
図 1 — 同じユーザー、同じエージェント。scope に無い操作は実行時に 403 で落ちる。確認ダイアログは出ない。出典:Zenn・icoxfog417 氏(AWS Japan)の記事より引用。

ここまでが処方箋の明るい面で、記事はコストも書いています。接続先の認可サーバーが urn:ietf:params:oauth:grant-type:token-exchange を受け付ける必要があり、著者の見立てでは 執筆時点で大半が未対応です。記事の AWS 実装でも、既存の Cognito にはグラント種別を追加できない ため、交換エンドポイント(/oauth2/token)を Lambda で新設しています。新規に書くのは 「交換リクエストの受付」と「自前の署名鍵でのトークン発行」の二つで、JWT の検証とユーザー権限の 解決は既存 SSO の流用です。「認可サーバーを一つ足す」が、このアプローチの最低限のコストになります。

鍵そのものを配らない——300 秒の IAM ロール証明を短期トークンに

Retty の記事は、同じ発想をモデル提供者との間で実践したものです。図 2 の順に三段階で読めます。

Retty の図:AWS STS(OIDC トークン発行)→ アプリケーションサーバー → OpenAI(検証して短期トークン発行)の五つのステップ
図 2 — 鍵は存在しない。アプリが持つのは IAM ロールだけで、STS がそれを JWT にし、OpenAI がそれを短期トークンに交換する。出典:Retty Tech Blog・pikatenor 氏より引用。

第一に、OpenAI API を呼ぶ ECS 上のアプリは $OPENAI_API_KEY を持たず、IAM ロールだけを持ちます。 第二に、AWS STS(一時認証情報を発行するサービス)がそのロールを証明する短命の JWT(署名付きの トークン)を発行します。この機能が IAM の Outbound Identity Federation(OIF)です。第三に、 OpenAI の Workload Identity Federation(WIF)がその JWT を検証し、短期アクセストークンに 交換します。

数字を拾います。STS に発行させる JWT の有効期間は、IAM ポリシーの条件で 300 秒以下に 固定されています。300 は Retty の恣意的な値ではなく、STS の GetWebIdentityToken既定値 (指定可能範囲は 60〜3600 秒)であり、OpenAI の AWS 向け公式ガイドの例も同じ 300 秒です。

condition {
  test     = "ForAllValues:StringEquals"
  variable = "sts:IdentityTokenAudience"
  values   = ["https://api.openai.com/v1"]
}
condition {
  test     = "NumericLessThanEquals"
  variable = "sts:DurationSeconds"
  values   = ["300"]
}

OpenAI 側はこの JWT を受け取り、issaudsub(発行者・宛先・主体)の三つと Service Account ID とが、事前登録済みのマッピングに一致するかを検証してから短期トークンを発行します。交換リクエストは、OpenAI の API リファレンスに ある通り RFC 8693 の形で、エンドポイントは auth.openai.com です。

curl https://auth.openai.com/oauth/token \
  -H "Content-Type: application/json" \
  -d '{
    "grant_type": "urn:ietf:params:oauth:grant-type:token-exchange",
    "subject_token_type": "urn:ietf:params:oauth:token-type:jwt",
    "subject_token": "<STS が発行した JWT>",
    "identity_provider_id": "idp_xxxxxxxx",
    "service_account_id": "user-XXXXXXXX"
  }'

発行されるトークンの実効寿命は、上限の 3600 秒ではなく外部 JWT の残り時間で決まります。 OpenAI のリファレンスが「アクセストークンは最長 1 時間で失効し、交換に使った外部トークンより 長生きしない」と明記しているためで、STS の JWT を 300 秒に絞った構成では 300 秒側に引っ張られる と読むのが安全です(筆者は未検証)。Retty の記事の脚注にある「デフォルトでは 3600 秒の expires_in」はリファレンスのレスポンス例とも一致しており、上限の値として正しい。上限と実効値の 違いです。Retty の SDK は「渡した JWT と交換したアクセストークンのどちらか長い方の有効期限が切れた タイミング」で再取得する、と記事にあります。どちらにせよ、鍵は「配る」ものから「数分ごとに 取り直す」ものに変わっています。

AWS Japan の記事が社内システム向けに自作した交換エンドポイントを、OpenAI は自社 API の 入口として提供している、と読めば二つの記事はつながります。そしてマッピング画面には、 この記事の主題にとって重要な欄がもう一つあります。

OpenAI の Create mapping 画面:Key/Value に sub・aud・iss、下部に Permissions の All / Restricted / Read only
図 3 — マッピングは「誰が」(sub)「どこ宛に」(aud)「誰の署名で」(iss)の三点一致。末尾の Permissions で、発行するトークンの権限をさらに狭められる。出典:Retty Tech Blog・pikatenor 氏より引用。

Permissions です。OpenAI のガイドでは、 マッピング単位で API 権限を付け、発行されるトークンをその範囲に縮小できる、とあります (マッピングに権限を書かなければ Service Account の権限がそのまま乗る)。アイデンティティの 交換で「短期」を、マッピングの権限で「スコープ」を、鍵の形に入れているわけです。なお、リクエスト 本体に scope を書いても権限は付きません——リファレンスに「scope value in the request body doesn’t grant access」とある通り、範囲を決めるのはマッピング側だけです。

対応範囲は二層で読む必要があります。プラットフォームとして受け付ける IdP は、 公式ガイドに載る 7 種——Kubernetes、AWS、Microsoft Azure、Google Cloud、Oracle Cloud Infrastructure、GitHub Actions、 SPIFFE(JWT-SVID)——と X.509 証明書(ベータ)で、載っていない OIDC 発行者は「contact us」扱いです。 一方 SDK に同梱された SubjectTokenProvider は、Retty の記事によれば執筆時点の openai-java 4.51.0 で Kubernetes / Azure / GCP の三つだけで、AWS OIF 向けは自作になっています。

コストも記事に書いてあります。自作した STS 呼び出しに加え、トークン交換レスポンスの scope が 既存の Jackson 設定で解析できず、FAIL_ON_UNKNOWN_PROPERTIES を無効にして回避しています。 新しい認証方式の初期費用は、たいてい SDK の穴として請求されます。

無制限キーは受け付けない——提供者側が鍵の形を強制し始めた

三つ目は方針の話です。Google の公式ドキュメントによれば、Gemini API は standard キーと authorization(auth)キーの二種類を持ち、後者へ移行中です。要点を原文の順に並べます。

  • standard キーはリクエストを課金・クォータのために Google Cloud プロジェクトへ 紐付けるだけで、呼び出し元を識別しない
  • auth キーはサービスアカウントに直接束縛され、リクエストはそのサービスアカウントの権限で 処理される。既定で Gemini API に制限され、漏洩が検知されたキーは速やかに停止される
  • AI Studio で新規作成するキーは、すべて auth キーになる
  • 無制限の standard キーは拒否。明示的な制限が付いた standard キーは引き続き動く。開始日が 2026-06-19 であることは、ドキュメント本文ではなく公式フォーラムの告知(2026-06-24 転載)による
  • 2026-05-07 以降、長期間未使用の無制限キーはブロックされる
  • 2026 年 9 月、standard キー全体が拒否対象になり、auth キーへの移行が必須になる
Google 公式ドキュメント「Using Gemini API keys」の API key types 節:standard と authorization の違い
図 4 — 「standard キーは呼び出し元を識別しない」と提供者自身が書いている。出典:Google AI for Developers

ここで起きているのは、利用者が善意で鍵を制限するのを待つのをやめ、形のない鍵を提供者が 受け付けなくするという転換です。AWS Japan と Retty が「鍵に形を与える」手順を書いた週に、 Google は「形のない鍵は捨てる」を実行に移していました。

三点セット——三つの一次情報を一枚の表に

リストカード:短期——鍵を配らず IdP の証明を数分のトークンに交換する/スコープ——エンドポイント・操作・モデルを鍵の中に書く/上限——鍵ごとの予算とレート
図 5 — エージェントに渡す鍵の三点セット。一つ欠けると、残り二つの意味が薄れます。

三つの出来事を同じ軸で並べます。評価軸は「漏れたときの影響範囲(blast radius)」「ローテーション の手間」「使い過ぎの上限」の三つです。

鍵の形漏洩時の影響範囲ローテーション使い過ぎの上限今週の該当例
長期 API キー(無制限)プロジェクト全体、検知まで無期限人手、デプロイを伴う請求書で気づくGemini が 6/19 に拒否した形
制限付き standard キー制限した API の範囲内、ただし呼び出し元を識別しない依然として人手プロジェクト単位のクォータGemini で 2026 年 9 月まで動く形
auth キー(サービスアカウント束縛)束縛先サービスアカウントの権限内、漏洩検知で速やかに停止依然として人手プロジェクト単位のクォータGemini が 9 月以降に唯一残す形
フェデレーション短期トークン最長 1 時間、外部トークン次第で数分、しかも IAM ロール等に束縛自動で毎回更新マッピング権限で範囲は絞れるが金額上限は別の仕組みRetty の WIF、AWS Japan の OBO

最終行の「使い過ぎの上限」列にある ⚠ が、この記事で一番言いたい場所です。短期とスコープは、今週の三つの一次情報で すでに実装と方針の両方が揃いました。上限だけは、どの記事にも主役として出てきません。 OBO の scope は「何をしてよいか」であって「いくらまでか」ではなく、OpenAI のマッピング 権限も同様です。LLM の API は一回の呼び出しで数万トークンを消費し、エージェントはそれを ループで回します。鍵の形に「いくらまで」が入っていない限り、漏洩しなくても自分の エージェントの暴走で請求が走ります。リトライの無限ループは、筆者の経験では漏洩より 先に来ます。

請求書——本番に持ち込む費用

三点セットを本番に持ち込む費用を、先に並べておきます。

  1. 認可サーバー側の対応。OBO を受け付ける認可サーバーは、AWS Japan の記事の見立てで 大半が未対応。自社サービスなら交換エンドポイントを一つ足す(記事では Lambda 一本)、 他社サービスなら相手が対応するまで待つ、の二択です。
  2. SDK の穴。Retty の記事が示した通り、提供者の公式 SDK でも IdP によっては SubjectTokenProvider を自作する必要があり、レスポンスの解析で足を取られます。 公式ガイドが「サポート済み」と書く IdP 以外は、自作分の保守が毎バージョン付いてきます。
  3. 時計と有効期限のデバッグ。外部 JWT とアクセストークンという二つの有効期限が走る構成では、 障害の切り分けに「どちらが切れたか」が必ず入ります。自作クライアントではその再取得ロジックを 自分で書くことになります。細かい罠も一つ:STS の GetWebIdentityToken はグローバル エンドポイントでは使えず、リージョナルエンドポイント必須です。
  4. ローカル開発の抜け道。STS もクラウドのメタデータサービスも無い開発機では、結局どこかに 長期キーが残りがちです。 ここを放置すると、本番だけ三点セット、開発機は無制限キー、という一番漏れやすい形になります。
  5. 上限の別建て。三点セットの表で述べた通り、金額とレートの上限は認可の仕組みの外側で 持つ必要があり、鍵単位の予算を数えるコンポーネントが一つ増えます。プロバイダ側のプロジェクト 単位の予算アラートは今日使える代替ですが、粒度は「鍵ごと」ではなく「プロジェクトごと」です。

主要なモデル提供経路について、長期キーを使わずに呼べる経路を公式ドキュメントで確認できた範囲で 表にしておきます。載っていない提供者は未確認です。

提供経路長期キー無しの経路状態
OpenAI APIWorkload Identity Federation(OIDC JWT → 短期トークン、公式ガイド提供中、7 種の IdP + X.509 ベータ
Gemini API(AI Studio)auth キー(サービスアカウント束縛、漏洩検知で停止、公式長期キーだがアイデンティティ付き。standard キーは 9 月で終了
Azure OpenAIMicrosoft Entra ID / マネージド ID(DefaultAzureCredential公式提供中。短期トークン、秘密の保管なし
Amazon BedrockIAM 認証(SigV4)が既定。API キーは短期(最長 12 時間、IAM 主体の権限を継承)と長期の二種、公式は長期を「探索用途のみ」と明記提供中。bedrock:CallWithBearerToken でキー種別ごとに拒否も可能
Vertex AIApplication Default Credentials(サービスアカウント / Workload Identity Federation、公式提供中。API キーも別途提供されるが、その制限の詳細は筆者未確認

ゲートウェイ側から見ると

LLM ゲートウェイは、この三点セットを実装する場所として都合がいい位置にいます。アプリと モデル提供者の間に立ち、鍵の発行者であり、同時にトークンを数える課金の場所でもあるからです。 私たち(pirouter のゲートウェイ)の場合、利用者に発行する API キーは設計上は、スコープ (どのモデル・どのエンドポイントを許すか)と上限(キーごとの予算とレート)を鍵の属性として持ち、 利用者側が持つ OIDC JWT を短期トークンに交換する入口も、上で見た OpenAI の WIF と同じ RFC 8693 の形に寄せる方針です。ゲートウェイ自身が上流プロバイダに対して持つ鍵についても、 正直に書いておきます。集約した分だけ漏洩時の影響範囲は大きく、だからこそ上流側に長期キー無しの 経路(上の表)があるものはそれを使い、無いものは長期キーを一か所に閉じ込めてローテーションと 監査の対象をそこだけにする、という設計です。今日ここで「対応している」とは書きません。書けるのは、 今週の三つの一次情報を読んだ後で、この三点を鍵の形から外す理由が見当たらなかった、ということだけです。

月曜にやる三つ

  • 自社の認可サーバーの /.well-known/oauth-authorization-server(Entra ID のように /.well-known/openid-configuration しか出さない IdP もあるので両方)を開き、 grant_types_supportedtoken-exchange があるか見る。無ければ、AWS Japan の記事の要件表で 「新規」になる行を数える。主要 IdP の対応状況は、筆者は今週確認できていません。
  • 上の提供経路の表を自社の一覧に広げる。経路が無い提供者は、そのキーの制限とローテーション周期を 今の値で記録しておく。
  • 鍵ごとの月額上限を、どのコンポーネントが数えているかを一行で書く。書けなければ、三点セットの 三つ目はまだ無い、ということです。