Login ChatGPT et authentification
Avant de faire tourner le modèle, codex doit obtenir des identifiants OpenAI. Cette couche couvre tout ce qui se passe entre « l'utilisateur tape codex login » et « auth.json écrit dans $CODEX_HOME » : cinq entrées — navigateur OAuth, PKCE, device code, API key, access token — partagent la même énumération CodexAuth et la même machine à états AuthManager. Le crate login/ gère le protocole d'auth pur ; cli/src/login.rs écrit le résultat sur disque et renvoie du feedback sur stderr.
Responsabilités
- Unifier plusieurs formes d'identifiants : l'énum
CodexAuthcouvre API key, ChatGPT OAuth, tokens ChatGPT externes, JWT agent identity, personal access token (PAT), Bedrock API key — sept modes.codex-rs/login/src/auth/manager.rs:71-79 - OAuth navigateur + PKCE :
run_login_serverdémarre un callback server local, génère la paire PKCE et le state, construitauth_urlpour le navigateur, attend la callback pour échanger le code contre un token.codex-rs/login/src/server.rs:151-197 - Flux device code :
run_device_code_logindemande d'abord un user_code à/deviceauth/usercode, affiche la verification_url dans le terminal pour que l'utilisateur la saisisse dans un navigateur, puis poll le endpoint token.codex-rs/login/src/device_code_auth.rs:234-238 - Enrobage CLI :
run_login_with_chatgpt/run_login_with_api_key/run_login_with_access_token/run_login_with_device_codenettoient les anciens identifiants, montent le file logging, vérifientforced_login_methodavant lancement, puisstd::process::exiten fin.codex-rs/cli/src/login.rs:167-196 - Reload et révocation :
AuthManager::reloadrelitauth.jsonaprès qu'un processus externe a rafraîchi le token ;logout_with_revokeappelle le endpoint OAuth revoke puis nettoie les identifiants locaux.codex-rs/login/src/auth/manager.rs:877-925
Motivations de conception
Au début, codex ne supportait que deux chemins : login ChatGPT via navigateur et API key. Le login ChatGPT passe par OAuth + PKCE : on démarre un callback server local, on génère code_verifier / code_challenge, on saute vers le navigateur ; OpenAI rappelle http://localhost:PORT/auth/callback?code=...&state=..., le server utilise le state contre le CSRF et le code_verifier pour échanger le token. Ce flux exige une machine avec navigateur ; les environnements SSH distants ne peuvent pas l'utiliser.
D'où le flux device code (device_code_auth.rs) : l'utilisateur voit dans son terminal https://chatgpt.com/device et un user_code, qu'il saisit dans n'importe quel navigateur. run_login_with_device_code_fallback_to_browser ajoute un fallback : on tente d'abord device code ; si le server répond 404 (device auth non activé côté backend), on retombe sur le flux navigateur.
CodexAuth est une énum plutôt qu'un trait object, car les structures de données des modes diffèrent énormément — API key c'est une simple chaîne, ChatGPT OAuth a un triplet access_token / refresh_token / id_token, agent identity est un JWT. L'énum rend auth_mode() immédiat à matcher ; ajouter un mode c'est ajouter une variante plutôt que toucher à une vtable.
Les politiques AgentIdentityAuthPolicy::JwtOnly vs ChatGptAuth décident « quand l'utilisateur est déjà loggué via ChatGPT, doit-on enregistrer automatiquement un agent identity ». Par défaut ChatGptAuth autorise cette mise à niveau implicite, pour que les long-running task n'aient pas besoin d'un login agent identity séparé.
Fichiers clés
codex-rs/login/src/auth/manager.rs:71-79 — pub enum CodexAuth, la racine des sept modes d'identifiants.codex-rs/login/src/auth/manager.rs:929-977 — login_with_access_token, dispatche selon la forme du token en PAT ou agent identity JWT.codex-rs/login/src/server.rs:68-148 — ServerOptions / LoginServer / ShutdownHandle, config et cycle de vie du server OAuth navigateur.codex-rs/login/src/device_code_auth.rs:20-34 — structure DeviceCode, porte verification_url, user_code, device_auth_id, intervalle de polling.codex-rs/cli/src/login.rs:52-111 — init_login_file_logging, monte une layer tracing minimale file-backed écrivant codex-login.log, ne réutilise pas volontairement la stack télémétrie lourde de la TUI.codex-rs/cli/src/login.rs:119-135 — clear_existing_auth_before_login, fait un logout avant login pour éviter qu'un vieux token ne traîne.codex-rs/tui/src/local_chatgpt_auth.rs:17-59 — load_local_chatgpt_auth, helper de test TUI : extrait ChatGPT access_token / account_id / plan_type du auth.json local.CodexAuth est la seule source de vérité des identifiants ; client.rs, provider, télémétrie en aval matchent tous dessus. Ajouter un mode d'identifiants = ajouter une variante + des arms de match un peu partout.
// 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 est une fine enveloppe CLI : charger config → monter file logging → vérifier si forced_login_method interdit le chemin ChatGPT → nettoyer les anciens identifiants → démarrer le callback server. À noter : elle retourne ! (never), toutes les sorties font process::exit, l'appelant ne continue jamais.
// 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 est typique du « dispatch automatique selon la forme du token » : un même flag --with-access-token arrive, mais à l'intérieur on inspecte la structure du JWT pour choisir chemin PAT ou agent identity JWT. À l'écriture du AuthDotJson, le champ auth_mode diffère en conséquence, pour qu'un rollback vers une ancienne version de codex puisse encore désérialiser correctement.
// 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)
}Flux de données
Limites et échecs
forced_login_methodexclusif : un admin peut forcer API ou ChatGPT uniquement ; si la CLI détecte une incompatibilité, elle sort sur stderr exit 1, sans attendre qu'OAuth soit refusé à mi-chemin.codex-rs/cli/src/login.rs:172-175- Fallback device code non supporté :
run_login_with_device_code_fallback_to_browsertente d'abord device code ; si elle reçoitErrorKind::NotFound, le backend n'a pas activé device auth, on retombe automatiquement sur le flux navigateur server ; les autres erreurs restent des échecs.codex-rs/cli/src/login.rs:389-419 - Permissions de
auth.jsonà l'écriture : sous Unix,OpenOptionsExt::mode(0o600)force lecture par le propriétaire uniquement, pour éviter que des utilisateurs du même groupe ne voient les identifiants.codex-rs/cli/src/login.rs:69-78 - Restriction workspace PAT :
ensure_personal_access_token_workspace_allowedvérifie au login PAT queforced_chatgpt_workspace_idcorrespond, pour empêcher l'usage d'un PAT hors de son workspace.codex-rs/login/src/auth/manager.rs:979-985
Récapitulatif
Le crate login/ unifie cinq entrées de login via l'énum CodexAuth et la machine à états AuthManager : OAuth navigateur + PKCE pour l'usage local, device code pour l'usage distant, API key / PAT / agent identity JWT pour les scénarios automatisés. cli/src/login.rs est une fine enveloppe qui monte le file logging, nettoie les anciens identifiants et garde forced_login_method ; toutes les sorties font process::exit. Après écriture, auth.json est la source d'identifiants pour toutes les requêtes modèle ; voir Client LLM et Responses API. Une fois login terminé, l'utilisateur entre dans la TUI ou lance codex exec ; voir Entrée CLI et répartition des sous-commandes.