ChatGPT ログインと認証
codex はモデルを走らせる前に OpenAI のクレデンシャルを取得する必要がある。このレイヤーは「ユーザが codex login を叩く」から「auth.json が $CODEX_HOME に書き込まれる」までの全作業をこなす:ブラウザ OAuth、PKCE、device code、API key、access token の五つの入口が同じ CodexAuth 列挙型と AuthManager 状態機械を共有する。login/ crate は純粋な認証プロトコルを担い、cli/src/login.rs はプロトコルの産物をディスクに落として stderr でフィードバックを返す。
責務
- 複数のクレデンシャル形態の統一:
CodexAuth列挙型は API key、ChatGPT OAuth、外部 ChatGPT tokens、agent identity JWT、personal access token (PAT)、Bedrock API key の七つのモードをカバーする。codex-rs/login/src/auth/manager.rs:71-79 - ブラウザ OAuth + PKCE:
run_login_serverはローカルで callback server を立て、PKCE pair と state を生成し、auth_urlを組み立ててブラウザにリダイレクトし、コールバックで code を受け取って token と交換する。codex-rs/login/src/server.rs:151-197 - Device code フロー:
run_device_code_loginはまず/deviceauth/usercodeに user_code を申請し、ターミナルに verification_url を表示してユーザにブラウザで入力させ、その後 token エンドポイントをポーリングする。codex-rs/login/src/device_code_auth.rs:234-238 - CLI 入口のラップ:
run_login_with_chatgpt/run_login_with_api_key/run_login_with_access_token/run_login_with_device_codeは実行前に古いクレデンシャルを消し、ファイルログを設定し、forced_login_methodでそのパスが許可されているか検証して、完了後にstd::process::exitする。codex-rs/cli/src/login.rs:167-196 - Auth の再読み込みと失効:
AuthManager::reloadは外部プロセスが token をリフレッシュした後にauth.jsonを再読み込みし、logout_with_revokeは OAuth revoke エンドポイントを呼んでからローカルのクレデンシャルを消す。codex-rs/login/src/auth/manager.rs:877-925
設計動機
codex は初期には ChatGPT ブラウザログインと API key の二つのルートだけをサポートしていた。ChatGPT ログインは OAuth + PKCE で動く:ローカルで callback server を立て、code_verifier / code_challenge を生成し、ブラウザに飛ばした上で OpenAI が http://localhost:PORT/auth/callback?code=...&state=... でコールバックする。server は state で CSRF を防ぎ、code_verifier で token と交換する。このフローは手元にブラウザが必要で、リモート SSH 環境では使えない。
そのため device code flow (device_code_auth.rs) が追加された:ユーザはターミナルで https://chatgpt.com/device と user_code を見て、任意のブラウザで入力すればよい。run_login_with_device_code_fallback_to_browser はさらにフォールバックを行い、まず device code を試し、server が 404 を返した場合(バックエンドが device auth を有効にしていない場合)はブラウザ server フローに戻る。
CodexAuth が trait オブジェクトではなく列挙型なのは、各クレデンシャルモードのデータ構造が大きく異なるからだ——API key は文字列一つ、ChatGPT OAuth は access_token / refresh_token / id_token の三つ組、agent identity は JWT。列挙型だと auth_mode() のようなマッチが一目で分かり、新モードの追加は vtable を変えるのではなくバリアントを増やすだけで済む。
AgentIdentityAuthPolicy::JwtOnly と ChatGptAuth の二つのポリシーは「ユーザがすでに ChatGPT でログインしている時、自動的に agent identity を登録するか」を決める。デフォルトの ChatGptAuth はこの暗黙のアップグレードを許可し、long-running task で別途 agent identity にログインし直す必要をなくす。
主要ファイル
codex-rs/login/src/auth/manager.rs:71-79 — pub enum CodexAuth。七つのクレデンシャルモードの根。codex-rs/login/src/auth/manager.rs:929-977 — login_with_access_token。token の形態に応じて PAT か agent identity JWT にディスパッチ。codex-rs/login/src/server.rs:68-148 — ServerOptions / LoginServer / ShutdownHandle。ブラウザ OAuth server の設定とライフサイクル。codex-rs/login/src/device_code_auth.rs:20-34 — DeviceCode 構造体。verification_url、user_code、device_auth_id、ポーリング間隔を保持。codex-rs/cli/src/login.rs:52-111 — init_login_file_logging。最小の file-backed tracing layer を設定し codex-login.log に書く。TUI の重い telemetry stack は意図的に再利用しない。codex-rs/cli/src/login.rs:119-135 — clear_existing_auth_before_login。ログイン前に logout し、古い token が残るのを防ぐ。codex-rs/tui/src/local_chatgpt_auth.rs:17-59 — load_local_chatgpt_auth。TUI のテスト補助:ローカルの auth.json から ChatGPT access_token / account_id / plan_type を抽出。CodexAuth がクレデンシャルの唯一の真実の情報源で、後続の client.rs、provider、telemetry は全てここから match でモードを取り出す。新しいクレデンシャルを追加するにはバリアントを増やし、各所に match arm を足すだけだ。
// login/src/auth/manager.rs:71-79 — 七种凭据模式
pub enum CodexAuth {
ApiKey(ApiKeyAuth),
Chatgpt(ChatgptAuth),
ChatgptAuthTokens(ChatgptAuthTokens),
Headers(AuthHeaders),
AgentIdentity(AgentIdentityAuth),
PersonalAccessToken(PersonalAccessTokenAuth),
BedrockApiKey(BedrockApiKeyAuth),
}run_login_with_chatgpt は CLI 層の薄いラッパーで、config を読み込み、ファイルログを設定し、forced_login_method で ChatGPT パスが無効化されていないか確認し、古いクレデンシャルを消して callback server を立てる。戻り値は !(never)で、全ての出口が process::exit を呼び、呼び出し側に処理を続けさせない。
// cli/src/login.rs:167-184 — ChatGPT 登录入口,薄包装 + 硬退出
pub async fn run_login_with_chatgpt(cli_config_overrides: CliConfigOverrides) -> ! {
let config = load_config_or_exit(cli_config_overrides).await;
let _login_log_guard = init_login_file_logging(&config);
tracing::info!("starting browser login flow");
if matches!(config.forced_login_method, Some(ForcedLoginMethod::Api)) {
eprintln!("{CHATGPT_LOGIN_DISABLED_MESSAGE}");
std::process::exit(1);
}
let forced_chatgpt_workspace_id = config.forced_chatgpt_workspace_id.clone();
match login_with_chatgpt(
config.codex_home.to_path_buf(),
forced_chatgpt_workspace_id,
config.cli_auth_credentials_store_mode,
config.auth_keyring_backend_kind(),
config.auth_route_config(),
).await { /* Ok/Err 分别 exit */ }
}login_with_access_token は「token の形態で自動ディスパッチ」の典型例——同じ --with-access-token flag から入っても、内部で JWT 構造を判定して PAT パスか agent identity JWT パスに振り分ける。AuthDotJson をディスクに書く時の auth_mode フィールドも異なり、古い codex バージョンにロールバックしても正しくデシリアライズできる。
// login/src/auth/manager.rs:929-970 — access token 自动分派 PAT vs AgentIdentity
pub async fn login_with_access_token(/* ... */) -> std::io::Result<()> {
let auth_dot_json = match classify_codex_access_token(access_token) {
CodexAccessToken::PersonalAccessToken(access_token) => {
let auth = PersonalAccessTokenAuth::load(access_token, auth_route_config).await?;
ensure_personal_access_token_workspace_allowed(forced_chatgpt_workspace_id, &auth)?;
AuthDotJson { auth_mode: None, personal_access_token: Some(access_token.to_string()), .. }
}
CodexAccessToken::AgentIdentityJwt(jwt) => {
verified_record_from_jwt(jwt, &base_url, auth_route_config).await?;
AuthDotJson { auth_mode: Some(AuthMode::AgentIdentity),
agent_identity: Some(AgentIdentityStorage::Jwt(jwt.to_string())), .. }
}
};
save_auth(codex_home, &auth_dot_json, auth_credentials_store_mode, keyring_backend_kind)
}データフロー
境界と失敗
forced_login_methodの排他:管理者は API か ChatGPT の一方のみを強制できる。CLI は不一致を検出した時点で stderr にエラーを出して exit 1 し、OAuth を途中まで走らせてから拒否されるのを防ぐ。codex-rs/cli/src/login.rs:172-175- device code 非対応時のフォールバック:
run_login_with_device_code_fallback_to_browserはまず device code を試し、ErrorKind::NotFoundが返ればバックエンド未対応として自動的にブラウザ server フローに戻る。他のエラーは失敗として扱う。codex-rs/cli/src/login.rs:389-419 auth.jsonのファイル権限:Unix ではOpenOptionsExt::mode(0o600)で所有者のみ読めるようにし、クレデンシャルが同グループのユーザに見えるのを防ぐ。codex-rs/cli/src/login.rs:69-78- PAT の workspace 制限:
ensure_personal_access_token_workspace_allowedは PAT ログイン時にforced_chatgpt_workspace_idが一致するか検証し、PAT が workspace をまたいで誤用されるのを防ぐ。codex-rs/login/src/auth/manager.rs:979-985
まとめ
login/ crate は五つのログイン入口を CodexAuth 列挙型と AuthManager 状態機械に統一する:ブラウザ OAuth + PKCE はローカル向き、device code はリモート向き、API key / PAT / agent identity JWT は自動化シーン向き。cli/src/login.rs は薄いラッパーで、ファイルログの設定、古いクレデンシャルの消去、forced_login_method のゲートを担い、全ての出口が process::exit する。ディスクに落ちた auth.json は以降の全モデルリクエストのクレデンシャル元で、詳しくは LLM クライアントと Responses API を参照。ログイン完了後、ユーザは TUI に入るか codex exec を走らせる。 CLI 入口とサブコマンドディスパッチ も参照。