mirror of
https://github.com/openai/codex.git
synced 2026-11-13 21:30:47 +00:00
Add a Git credential helper for existing Codex logins (#60746)
## What changed Add the hidden `codex git-credential` command to supply an existing file-backed ChatGPT login as an ephemeral Bearer credential through Git's `authtype` protocol. Restrict credentials to HTTPS requests under `backend-api/codex/sites/git/` on `chatgpt.com` or `chatgpt-staging.com`, matching the configured origin. Check managed workspace policy and reject expired or changed logins before returning credentials. Ignore repository configuration when selecting authentication settings. The helper does not refresh tokens or modify the saved login; `store` and `erase` are no-ops. Return `quit=true` when authentication is unavailable or fails so Git does not fall back to prompting. ## Testing Add coverage for capability advertisement before login, reading only the first credential record, and declining personal access tokens without an authentication API lookup. GitOrigin-RevId: 2f96af1eeb7d05e3d893367137103a7adbc02742
This commit is contained in:
@@ -46,6 +46,52 @@ fn read_auth_json(codex_home: &Path) -> Result<Value> {
|
||||
Ok(serde_json::from_str(&auth_json)?)
|
||||
}
|
||||
|
||||
const SITES_GIT_REQUEST: &str = "capability[]=authtype\nprotocol=https\nhost=chatgpt.com\npath=backend-api/codex/sites/git/fixture.git\n\n";
|
||||
|
||||
#[test]
|
||||
fn git_credential_advertises_authtype_before_login() -> Result<()> {
|
||||
let codex_home = TempDir::new()?;
|
||||
codex_command(codex_home.path())?
|
||||
.args(["git-credential", "capability"])
|
||||
.assert()
|
||||
.success()
|
||||
.stdout(contains("capability authtype\n"));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn git_credential_reads_only_the_first_record() -> Result<()> {
|
||||
let home = TempDir::new()?;
|
||||
codex_command(home.path())?
|
||||
.args(["git-credential", "get"])
|
||||
.write_stdin(format!(
|
||||
"protocol=https\nhost=chatgpt.com\n\n{SITES_GIT_REQUEST}"
|
||||
))
|
||||
.assert()
|
||||
.success()
|
||||
.stdout("");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn git_credential_declines_personal_access_tokens_without_a_lookup() -> Result<()> {
|
||||
let home = TempDir::new()?;
|
||||
write_file_auth_config(home.path())?;
|
||||
let auth_api = MockServer::start().await;
|
||||
codex_command(home.path())?
|
||||
.env_remove(OPENAI_FEDERATION_RULE_ID_ENV_VAR)
|
||||
.env_remove(OPENAI_IDENTITY_TOKEN_FILE_ENV_VAR)
|
||||
.env(CODEX_ACCESS_TOKEN_ENV_VAR, "at-fixture")
|
||||
.env("CODEX_AUTHAPI_BASE_URL", auth_api.uri())
|
||||
.args(["git-credential", "get"])
|
||||
.write_stdin(SITES_GIT_REQUEST)
|
||||
.assert()
|
||||
.success()
|
||||
.stdout("quit=true\n\n");
|
||||
assert!(auth_api.received_requests().await.unwrap().is_empty());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn login_with_api_key_reads_stdin_and_writes_auth_json() -> Result<()> {
|
||||
let codex_home = TempDir::new()?;
|
||||
|
||||
Reference in New Issue
Block a user