fix: replace --api-key with --with-api-key in codex login (#4646)

Previously, users could supply their API key directly via:

```shell
codex login --api-key KEY
```

but this has the drawback that `KEY` is more likely to end up in shell
history, can be read from `/proc`, etc.

This PR removes support for `--api-key` and replaces it with
`--with-api-key`, which reads the key from stdin, so either of these are
better options:

```
printenv OPENAI_API_KEY | codex login --with-api-key
codex login --with-api-key < my_key.txt
```

Other CLIs, such as `gh auth login --with-token`, follow the same
practice.
This commit is contained in:
Michael Bolin
2025-10-02 23:17:31 -07:00
committed by GitHub
parent 16b6951648
commit 69ac5153d4
3 changed files with 58 additions and 3 deletions

View File

@@ -9,6 +9,8 @@ use codex_core::config::ConfigOverrides;
use codex_login::ServerOptions; use codex_login::ServerOptions;
use codex_login::run_device_code_login; use codex_login::run_device_code_login;
use codex_login::run_login_server; use codex_login::run_login_server;
use std::io::IsTerminal;
use std::io::Read;
use std::path::PathBuf; use std::path::PathBuf;
pub async fn login_with_chatgpt(codex_home: PathBuf) -> std::io::Result<()> { pub async fn login_with_chatgpt(codex_home: PathBuf) -> std::io::Result<()> {
@@ -56,6 +58,33 @@ pub async fn run_login_with_api_key(
} }
} }
pub fn read_api_key_from_stdin() -> String {
let mut stdin = std::io::stdin();
if stdin.is_terminal() {
eprintln!(
"--with-api-key expects the API key on stdin. Try piping it, e.g. `printenv OPENAI_API_KEY | codex login --with-api-key`."
);
std::process::exit(1);
}
eprintln!("Reading API key from stdin...");
let mut buffer = String::new();
if let Err(err) = stdin.read_to_string(&mut buffer) {
eprintln!("Failed to read API key from stdin: {err}");
std::process::exit(1);
}
let api_key = buffer.trim().to_string();
if api_key.is_empty() {
eprintln!("No API key provided via stdin.");
std::process::exit(1);
}
api_key
}
/// Login using the OAuth device code flow. /// Login using the OAuth device code flow.
pub async fn run_login_with_device_code( pub async fn run_login_with_device_code(
cli_config_overrides: CliConfigOverrides, cli_config_overrides: CliConfigOverrides,

View File

@@ -7,6 +7,7 @@ use codex_chatgpt::apply_command::ApplyCommand;
use codex_chatgpt::apply_command::run_apply_command; use codex_chatgpt::apply_command::run_apply_command;
use codex_cli::LandlockCommand; use codex_cli::LandlockCommand;
use codex_cli::SeatbeltCommand; use codex_cli::SeatbeltCommand;
use codex_cli::login::read_api_key_from_stdin;
use codex_cli::login::run_login_status; use codex_cli::login::run_login_status;
use codex_cli::login::run_login_with_api_key; use codex_cli::login::run_login_with_api_key;
use codex_cli::login::run_login_with_chatgpt; use codex_cli::login::run_login_with_chatgpt;
@@ -139,7 +140,18 @@ struct LoginCommand {
#[clap(skip)] #[clap(skip)]
config_overrides: CliConfigOverrides, config_overrides: CliConfigOverrides,
#[arg(long = "api-key", value_name = "API_KEY")] #[arg(
long = "with-api-key",
help = "Read the API key from stdin (e.g. `printenv OPENAI_API_KEY | codex login --with-api-key`)"
)]
with_api_key: bool,
#[arg(
long = "api-key",
value_name = "API_KEY",
help = "(deprecated) Previously accepted the API key directly; now exits with guidance to use --with-api-key",
hide = true
)]
api_key: Option<String>, api_key: Option<String>,
/// EXPERIMENTAL: Use device code flow (not yet supported) /// EXPERIMENTAL: Use device code flow (not yet supported)
@@ -298,7 +310,13 @@ async fn cli_main(codex_linux_sandbox_exe: Option<PathBuf>) -> anyhow::Result<()
login_cli.client_id, login_cli.client_id,
) )
.await; .await;
} else if let Some(api_key) = login_cli.api_key { } else if login_cli.api_key.is_some() {
eprintln!(
"The --api-key flag is no longer supported. Pipe the key instead, e.g. `printenv OPENAI_API_KEY | codex login --with-api-key`."
);
std::process::exit(1);
} else if login_cli.with_api_key {
let api_key = read_api_key_from_stdin();
run_login_with_api_key(login_cli.config_overrides, api_key).await; run_login_with_api_key(login_cli.config_overrides, api_key).await;
} else { } else {
run_login_with_chatgpt(login_cli.config_overrides).await; run_login_with_chatgpt(login_cli.config_overrides).await;

View File

@@ -5,9 +5,17 @@
If you prefer to pay-as-you-go, you can still authenticate with your OpenAI API key: If you prefer to pay-as-you-go, you can still authenticate with your OpenAI API key:
```shell ```shell
codex login --api-key "your-api-key-here" printenv OPENAI_API_KEY | codex login --with-api-key
``` ```
Alternatively, read from a file:
```shell
codex login --with-api-key < my_key.txt
```
The legacy `--api-key` flag now exits with an error instructing you to use `--with-api-key` so that the key never appears in shell history or process listings.
This key must, at minimum, have write access to the Responses API. This key must, at minimum, have write access to the Responses API.
## Migrating to ChatGPT login from API key ## Migrating to ChatGPT login from API key