6.9 KiB
Codex OAuth 認証の流用
Status: Reviewed / Approved(詳細は llm-auth-codex-oauth.review.md)
背景
決定済み方針(docs/plan/llm_providers.md)で、ChatGPT サブスクリプションの OAuth トークンを流用して OpenAI Responses API を叩く経路を第一級サポートとする。OpenAI は Codex CLI を Apache-2.0 で公開し、ChatGPT OAuth の第三者ツール利用を service terms で名指し禁止していない(互換経路)。
Codex CLI の実装(github.com/openai/codex、codex-rs/login/ 配下)から以下が確定:
- トークンは
~/.codex/auth.jsonに{ auth_mode, tokens: { id_token (JWT 文字列), access_token (JWT), refresh_token, account_id }, last_refresh }形式で保存。OPENAI_API_KEYフィールドも同居する場合あり - リクエストは
https://chatgpt.com/backend-api/v1/responsesに投げる(Responses API wire) - 認証ヘッダは
Authorization: Bearer <access_token>+ChatGPT-Account-Id: <account_id>(FedRAMP 組織ならX-OpenAI-Fedramp: true) - リフレッシュは
https://auth.openai.com/oauth/tokenに{ client_id: "app_EMoamEEZ73f0CkXaXp7hrann", grant_type: "refresh_token", refresh_token }を JSON POST。response は{ id_token?, access_token?, refresh_token? }、401 body のerror.codeがrefresh_token_expired|reused|invalidatedで永続失敗を分類 - proactive refresh の判定: access_token JWT の
expclaim がnow以下、fallback でlast_refresh < now - 8 days(バッファなし) - Codex CLI 自身はファイルロックを取らず、(a) プロセス内
AsyncMutex(b) refresh 前の guarded reload (account_id 比較で他プロセスの先行更新を検知) (c) 書込前の再 load + diff merge で並行動作を吸収 - Codex CLI の credentials store はデフォルト
File(auth.json)。Keyring/Autoモードは opt-in
要件
-
AuthRef::CodexOAuthの追加:manifest::AuthRefに ChatGPT OAuth バリアントを追加(型定義済み、provider 側で実装する) -
トークン読み取り:
~/.codex/auth.jsonを読み、以下を取り出すtokens.access_token/tokens.refresh_token/tokens.account_idlast_refresh(fallback 期限判定用)tokens.id_tokenの JWT payload を base64url decode しhttps://api.openai.com/authclaim からchatgpt_account_is_fedramp(bool) とchatgpt_plan_typeを取得(署名検証なし)
-
ヘッダ注入:
HttpTransportがAuthRef::CodexOAuthを解決するとき以下を組み立てるAuthorization: Bearer <access_token>ChatGPT-Account-Id: <account_id>- FedRAMP 組織なら
X-OpenAI-Fedramp: true
実装方針: llm-worker 側で
ResolvedAuth::Custom(Arc<dyn AuthProvider>)バリアントとAuthProvidertrait(async でリクエスト毎にヘッダを返す)を新設し、HttpTransportのAuthRequirement::Custom経路から呼ぶ。Codex 固有ロジック(auth.json 読取・refresh)はcrates/provider/src/codex_oauth/に置く。 -
base_url の自動適用:
AuthRef::CodexOAuth指定時、ModelConfig.base_url未指定ならhttps://chatgpt.com/backend-apiを既定とする。明示指定(API key 経路と共用したい場合のhttps://api.openai.com等)は尊重。scheme側の既定(https://api.openai.com)は変えず、build_clientで auth に応じて差し替える。 -
トークンリフレッシュ:
- 送信前に proactive チェック: access_token JWT の
expclaim がnow以下、fallback でlast_refresh + 8 days < now。バッファは持たせない(Codex 準拠)。401 駆動の retry は将来拡張 - refresh は
https://auth.openai.com/oauth/tokenに上記 body を POST、response のaccess_token/id_token/refresh_tokenを auth.json に書き戻しlast_refresh = nowを更新 - 並行動作の排他: プロセス内
tokio::sync::Mutexを取った上で、refresh 直前に再 load し account_id が一致しないなら自分はスキップして読み直した値を採用(guarded reload)。書込時も再 load + diff merge(Codex CLI に揃える、ファイルロックは使わない) - 失敗時:
RefreshTokenError::Permanent(401 +refresh_token_expired|reused|invalidated)はClientError::Auth相当で「codex loginを再実行してください」のメッセージ、Transient(network 等) はそのまま伝播
- 送信前に proactive チェック: access_token JWT の
-
scheme/openai_responses との組合せで動作:
ModelConfig { scheme: OpenAIResponses, base_url: (既定), model_id: "gpt-5-codex" 等, auth: CodexOAuth }で ChatGPT 枠を使って Codex 相当の動作ができる -
完了時の動作: ChatGPT アカウント保持者が
codex login(File モード)済みの環境で insomnia を起動すると、追加設定なしで Codex と同じモデル(gpt-5-codex等)が利用可能
設計判断
auth.json の書き戻しと競合制御
ファイルロックは取らない。Codex CLI 自身も取っていない(単純な truncate write + mode 0o600)。代わりに guarded reload + 書込前 merge で吸収する。fs2 依存を増やさない方が筋が良い。
refresh token の失効時の挙動
Permanent 分類のエラーをそのまま ClientError::Auth { message } で返し、CLI/TUI 上で「codex login を再実行」のメッセージを表示する。insomnia 側で再ログインフローは持たない。
ChatGPT OAuth 専用モデル (gpt-5-codex 等)
scheme/openai_responses/capability.rs の静的テーブルに該当モデル ID を追加するのみ。API key 経路で使われたら OpenAI 側が 401 で弾くので動的フォールバックは不要。
Keyring モード対応
Scope 外。Codex CLI の cli_auth_credentials_store = "keyring" で保存しているユーザーには ~/.codex/config.toml を "file" に切り替えて codex login をやり直すよう案内する(auth.json 不在エラー時にメッセージで誘導)。
セキュリティと権限
~/.codex/auth.json のパーミッション(600)を尊重。書き込み時に OpenOptions で mode(0o600) を再設定する(Codex CLI と同じ)。
Scope 外
- Claude Pro/Max OAuth 経路(方針上非採用)
claude -pCLI fork 経路codex login自体の実装(Codex CLI に任せ、insomnia は auth.json を読むのみ)- Codex CLI の Keyring credentials store 対応
- 401 駆動の動的 refresh + retry(proactive のみで実装)
- ChatGPT backend の rate limit 観測(Retry-After 処理は HttpTransport 共通の責務)
依存
manifest::AuthRef::CodexOAuth(型のみ定義済み)llm_worker::llm_client::transport::HttpTransport/auth::AuthRequirement::Custom(経路の枠は用意済み)scheme/openai_responses(/v1/responseswire format、実装済み)