Login de ChatGPT y autenticación
Antes de correr el modelo, codex necesita credenciales de OpenAI. Esta capa hace todo lo que hay entre «el usuario escribe codex login» y que se escriba auth.json en $CODEX_HOME: cinco entradas — OAuth de navegador, PKCE, device code, API key, access token — comparten el mismo enum CodexAuth y la misma máquina de estados AuthManager. El crate login/ se ocupa del protocolo de autenticación puro; cli/src/login.rs vuelca el resultado del protocolo a disco y da feedback por stderr.
Responsabilidades
- Unificación de varias formas de credencial: el enum
CodexAuthcubre siete modos: API key, ChatGPT OAuth, tokens externos de ChatGPT, JWT de agent identity, personal access token (PAT), Bedrock API key.codex-rs/login/src/auth/manager.rs:71-79 - OAuth de navegador + PKCE:
run_login_serverlevanta un callback server local, genera el par PKCE y el state, componeauth_urlpara que el navegador salte, y al recibir el callback canjea el code por token.codex-rs/login/src/server.rs:151-197 - Flujo device code:
run_device_code_loginpide primero un user_code a/deviceauth/usercode, imprime en terminal la verification_url para que el usuario la introduzca en el navegador, y luego hace polling al endpoint de token.codex-rs/login/src/device_code_auth.rs:234-238 - Envoltorio de entrada CLI:
run_login_with_chatgpt/run_login_with_api_key/run_login_with_access_token/run_login_with_device_codelimpian credenciales viejas antes de correr, instalan file logging, validan segúnforced_login_methodsi esa ruta está permitida, y al terminar hacenstd::process::exit.codex-rs/cli/src/login.rs:167-196 - Reload y revocación de auth:
AuthManager::reloadreleeauth.jsondespués de que un proceso externo refresque el token;logout_with_revokellama al endpoint de OAuth revoke y luego limpia las credenciales locales.codex-rs/login/src/auth/manager.rs:877-925
Motivación de diseño
Al principio codex solo soportaba dos rutas: login de ChatGPT por navegador y API key. El login de ChatGPT va por OAuth + PKCE: se levanta un callback server local, se generan code_verifier / code_challenge, se salta al navegador y OpenAI responde a http://localhost:PORT/auth/callback?code=...&state=.... El server usa el state contra CSRF y canjea el code_verifier por token. Este flujo exige una máquina con navegador; en un entorno SSH remoto no se puede usar.
Por eso se añadió el flujo device code (device_code_auth.rs): el usuario ve en terminal https://chatgpt.com/device y un user_code, y lo introduce en cualquier navegador. run_login_with_device_code_fallback_to_browser añade además un fallback: prueba primero device code, y si el server devuelve 404 (el backend no tiene device auth abierto), retrocede al flujo con browser server.
CodexAuth es un enum y no un trait object, porque las estructuras de datos de cada modo de credencial son muy distintas: API key es una simple cadena; ChatGPT OAuth lleva una tripleta access_token / refresh_token / id_token; agent identity es un JWT. El enum hace que un auth_mode() de match se lea de un vistazo, y añadir un modo nuevo es añadir una variante en vez de tocar un vtable.
Las dos políticas AgentIdentityAuthPolicy::JwtOnly y ChatGptAuth deciden «cuando el usuario ya está logueado con ChatGPT, ¿se registra automáticamente un agent identity?». Por defecto ChatGptAuth permite esa subida implícita, para que las tareas long-running no necesiten un login aparte de agent identity.
Archivos clave
codex-rs/login/src/auth/manager.rs:71-79 — pub enum CodexAuth, raíz de los siete modos de credencial.codex-rs/login/src/auth/manager.rs:929-977 — login_with_access_token: según la forma del token, lo deriva a PAT o a JWT de agent identity.codex-rs/login/src/server.rs:68-148 — ServerOptions / LoginServer / ShutdownHandle: configuración y ciclo de vida del OAuth server de navegador.codex-rs/login/src/device_code_auth.rs:20-34 — struct DeviceCode, con verification_url, user_code, device_auth_id y polling interval.codex-rs/cli/src/login.rs:52-111 — init_login_file_logging: instala una capa de tracing mínima respaldada en archivo que escribe codex-login.log; no reutiliza el stack de telemetría pesado del TUI.codex-rs/cli/src/login.rs:119-135 — clear_existing_auth_before_login: hace logout antes del login para evitar tokens viejos rezagados.codex-rs/tui/src/local_chatgpt_auth.rs:17-59 — load_local_chatgpt_auth: helper de tests del TUI para extraer access_token / account_id / plan_type del auth.json local.CodexAuth es la única fuente de verdad de la credencial; todo lo de aguas abajo (client.rs, provider, telemetry) hace match contra este enum para distinguir el modo. Añadir un nuevo tipo de credencial es añadir una variante y los match arms correspondientes en cada sitio.
// login/src/auth/manager.rs:71-79 — siete modos de credencial
pub enum CodexAuth {
ApiKey(ApiKeyAuth),
Chatgpt(ChatgptAuth),
ChatgptAuthTokens(ChatgptAuthTokens),
Headers(AuthHeaders),
AgentIdentity(AgentIdentityAuth),
PersonalAccessToken(PersonalAccessTokenAuth),
BedrockApiKey(BedrockApiKeyAuth),
}run_login_with_chatgpt es un envoltorio fino a nivel CLI: carga config → instala file logging → comprueba si forced_login_method deshabilita la ruta ChatGPT → limpia credenciales viejas → arranca el callback server. Ojo: devuelve ! (never), todas sus salidas son process::exit, no deja que el llamador siga corriendo.
// cli/src/login.rs:167-184 — entrada de login ChatGPT, envoltorio fino + salida dura
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 hacen exit por separado */ }
}login_with_access_token es el ejemplo típico de «dispatch automático según la forma del token»: entra por el mismo flag --with-access-token, y por dentro, según la estructura del JWT, decide si va por la ruta PAT o por la de JWT de agent identity. Al volcar a disco AuthDotJson, el campo auth_mode también es distinto, para que versiones viejas de codex al hacer rollback también deserialicen correctamente.
// login/src/auth/manager.rs:929-970 — access token se deriva solo a 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)
}Flujo de datos
Bordes y fallos
forced_login_methodmutuamente excluyente: el administrador puede forzar solo API o solo ChatGPT; la CLI al detectar inconsistencia imprime a stderr y sale con exit 1, sin esperar a que OAuth se rechace a mitad.codex-rs/cli/src/login.rs:172-175- Fallback cuando device code no se soporta:
run_login_with_device_code_fallback_to_browserprueba primero device code; si devuelveErrorKind::NotFoundindica que el backend no lo tiene abierto y retrocede automáticamente al flujo de browser server. Otros errores se tratan como fallo.codex-rs/cli/src/login.rs:389-419 - Permisos al volcar
auth.json: en Unix,OpenOptionsExt::mode(0o600)fuerza a que solo el dueño pueda leer, evitando que usuarios del mismo grupo vean las credenciales.codex-rs/cli/src/login.rs:69-78 - Restricción de workspace en PAT:
ensure_personal_access_token_workspace_allowedvalida al hacer login con PAT siforced_chatgpt_workspace_idcoincide, evitando uso跨 workspace de PAT.codex-rs/login/src/auth/manager.rs:979-985
Resumen
El crate login/ unifica cinco entradas de login en el enum CodexAuth y la máquina de estados AuthManager: OAuth de navegador + PKCE para local, device code para remoto, API key / PAT / JWT de agent identity para escenarios de automatización. cli/src/login.rs es un envoltorio fino que se ocupa de instalar file logging, limpiar credenciales viejas y hacer de guardián con forced_login_method; todas sus salidas son process::exit. Tras el volcado, auth.json es la fuente de credenciales para todas las peticiones de modelo posteriores; ver Cliente LLM y Responses API. Tras el login, el usuario entra al TUI o corre codex exec; ver Entrada CLI y dispatch de subcomandos.