mirror of
https://github.com/openai/codex.git
synced 2026-11-13 14:46:14 +00:00
Support staging OAuth client ID overrides (#28257)
## Summary - allow app-server ChatGPT login to use a configured OAuth client ID - reuse the same client ID for refresh and revoke requests - cover staging login, refresh, and revoke request payloads ## Tests - `just test -p codex-login` - `just test -p codex-app-server login_account_chatgpt_uses_debug_oauth_overrides` - `just test -p codex-login logout_with_revoke_revokes_refresh_token_then_removes_auth` - `just fix -p codex-login` - `just fix -p codex-app-server` - `just fmt`
This commit is contained in:
1 parent
aae4c0b02b
commit
336f907ec1
8 files changed
+108
-31
No files matched your search
@@ -8,6 +8,7 @@ use codex_config::types::AuthCredentialsStoreMode;
|
||||
use codex_login::AuthDotJson;
|
||||
use codex_login::AuthKeyringBackendKind;
|
||||
use codex_login::AuthManager;
|
||||
use codex_login::CLIENT_ID_OVERRIDE_ENV_VAR;
|
||||
use codex_login::REFRESH_TOKEN_URL_OVERRIDE_ENV_VAR;
|
||||
use codex_login::RefreshTokenError;
|
||||
use codex_login::load_auth_dot_json;
|
||||
@@ -31,11 +32,12 @@ use wiremock::matchers::path;
|
||||
const INITIAL_ACCESS_TOKEN: &str = "initial-access-token";
|
||||
const INITIAL_REFRESH_TOKEN: &str = "initial-refresh-token";
|
||||
|
||||
#[serial_test::serial(auth_refresh)]
|
||||
#[serial_test::serial(auth_env)]
|
||||
#[tokio::test]
|
||||
async fn refresh_token_succeeds_updates_storage() -> Result<()> {
|
||||
skip_if_no_network!(Ok(()));
|
||||
|
||||
let _client_id_guard = EnvGuard::set(CLIENT_ID_OVERRIDE_ENV_VAR, "staging-client".to_string());
|
||||
let server = MockServer::start().await;
|
||||
Mock::given(method("POST"))
|
||||
.and(path("/oauth/token"))
|
||||
@@ -66,6 +68,16 @@ async fn refresh_token_succeeds_updates_storage() -> Result<()> {
|
||||
.await
|
||||
.context("refresh should succeed")?;
|
||||
|
||||
let requests = server.received_requests().await.unwrap_or_default();
|
||||
assert_eq!(
|
||||
serde_json::from_slice::<serde_json::Value>(&requests[0].body)?,
|
||||
json!({
|
||||
"client_id": "staging-client",
|
||||
"grant_type": "refresh_token",
|
||||
"refresh_token": INITIAL_REFRESH_TOKEN,
|
||||
})
|
||||
);
|
||||
|
||||
let refreshed_tokens = TokenData {
|
||||
access_token: "new-access-token".to_string(),
|
||||
refresh_token: "new-refresh-token".to_string(),
|
||||
@@ -97,7 +109,7 @@ async fn refresh_token_succeeds_updates_storage() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[serial_test::serial(auth_refresh)]
|
||||
#[serial_test::serial(auth_env)]
|
||||
#[tokio::test]
|
||||
async fn refresh_token_refreshes_when_auth_is_unchanged() -> Result<()> {
|
||||
skip_if_no_network!(Ok(()));
|
||||
@@ -163,7 +175,7 @@ async fn refresh_token_refreshes_when_auth_is_unchanged() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[serial_test::serial(auth_refresh)]
|
||||
#[serial_test::serial(auth_env)]
|
||||
#[tokio::test]
|
||||
async fn auth_refreshes_when_access_token_is_near_expiry() -> Result<()> {
|
||||
skip_if_no_network!(Ok(()));
|
||||
@@ -225,7 +237,7 @@ async fn auth_refreshes_when_access_token_is_near_expiry() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[serial_test::serial(auth_refresh)]
|
||||
#[serial_test::serial(auth_env)]
|
||||
#[tokio::test]
|
||||
async fn auth_skips_access_token_outside_refresh_window() -> Result<()> {
|
||||
skip_if_no_network!(Ok(()));
|
||||
@@ -263,7 +275,7 @@ async fn auth_skips_access_token_outside_refresh_window() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[serial_test::serial(auth_refresh)]
|
||||
#[serial_test::serial(auth_env)]
|
||||
#[tokio::test]
|
||||
async fn refresh_token_skips_refresh_when_auth_changed() -> Result<()> {
|
||||
skip_if_no_network!(Ok(()));
|
||||
@@ -324,7 +336,7 @@ async fn refresh_token_skips_refresh_when_auth_changed() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[serial_test::serial(auth_refresh)]
|
||||
#[serial_test::serial(auth_env)]
|
||||
#[tokio::test]
|
||||
async fn refresh_token_errors_on_account_mismatch() -> Result<()> {
|
||||
skip_if_no_network!(Ok(()));
|
||||
@@ -399,7 +411,7 @@ async fn refresh_token_errors_on_account_mismatch() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[serial_test::serial(auth_refresh)]
|
||||
#[serial_test::serial(auth_env)]
|
||||
#[tokio::test]
|
||||
async fn returns_fresh_tokens_as_is() -> Result<()> {
|
||||
skip_if_no_network!(Ok(()));
|
||||
@@ -448,7 +460,7 @@ async fn returns_fresh_tokens_as_is() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[serial_test::serial(auth_refresh)]
|
||||
#[serial_test::serial(auth_env)]
|
||||
#[tokio::test]
|
||||
async fn refreshes_token_when_access_token_is_expired() -> Result<()> {
|
||||
skip_if_no_network!(Ok(()));
|
||||
@@ -510,7 +522,7 @@ async fn refreshes_token_when_access_token_is_expired() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[serial_test::serial(auth_refresh)]
|
||||
#[serial_test::serial(auth_env)]
|
||||
#[tokio::test]
|
||||
async fn auth_reloads_disk_auth_when_cached_auth_is_stale() -> Result<()> {
|
||||
skip_if_no_network!(Ok(()));
|
||||
@@ -568,7 +580,7 @@ async fn auth_reloads_disk_auth_when_cached_auth_is_stale() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[serial_test::serial(auth_refresh)]
|
||||
#[serial_test::serial(auth_env)]
|
||||
#[tokio::test]
|
||||
async fn auth_reloads_disk_auth_without_calling_expired_refresh_token() -> Result<()> {
|
||||
skip_if_no_network!(Ok(()));
|
||||
@@ -634,7 +646,7 @@ async fn auth_reloads_disk_auth_without_calling_expired_refresh_token() -> Resul
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[serial_test::serial(auth_refresh)]
|
||||
#[serial_test::serial(auth_env)]
|
||||
#[tokio::test]
|
||||
async fn refresh_token_returns_permanent_error_for_expired_refresh_token() -> Result<()> {
|
||||
skip_if_no_network!(Ok(()));
|
||||
@@ -689,7 +701,7 @@ async fn refresh_token_returns_permanent_error_for_expired_refresh_token() -> Re
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[serial_test::serial(auth_refresh)]
|
||||
#[serial_test::serial(auth_env)]
|
||||
#[tokio::test]
|
||||
async fn refresh_token_does_not_retry_after_permanent_failure() -> Result<()> {
|
||||
skip_if_no_network!(Ok(()));
|
||||
@@ -758,7 +770,7 @@ async fn refresh_token_does_not_retry_after_permanent_failure() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[serial_test::serial(auth_refresh)]
|
||||
#[serial_test::serial(auth_env)]
|
||||
#[tokio::test]
|
||||
async fn refresh_token_does_not_retry_after_bad_request_reused_failure() -> Result<()> {
|
||||
skip_if_no_network!(Ok(()));
|
||||
@@ -827,7 +839,7 @@ async fn refresh_token_does_not_retry_after_bad_request_reused_failure() -> Resu
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[serial_test::serial(auth_refresh)]
|
||||
#[serial_test::serial(auth_env)]
|
||||
#[tokio::test]
|
||||
async fn refresh_token_reloads_changed_auth_after_permanent_failure() -> Result<()> {
|
||||
skip_if_no_network!(Ok(()));
|
||||
@@ -915,7 +927,7 @@ async fn refresh_token_reloads_changed_auth_after_permanent_failure() -> Result<
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[serial_test::serial(auth_refresh)]
|
||||
#[serial_test::serial(auth_env)]
|
||||
#[tokio::test]
|
||||
async fn refresh_token_returns_transient_error_on_server_failure() -> Result<()> {
|
||||
skip_if_no_network!(Ok(()));
|
||||
@@ -969,7 +981,7 @@ async fn refresh_token_returns_transient_error_on_server_failure() -> Result<()>
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[serial_test::serial(auth_refresh)]
|
||||
#[serial_test::serial(auth_env)]
|
||||
#[tokio::test]
|
||||
async fn unauthorized_recovery_reloads_then_refreshes_tokens() -> Result<()> {
|
||||
skip_if_no_network!(Ok(()));
|
||||
@@ -1068,7 +1080,7 @@ async fn unauthorized_recovery_reloads_then_refreshes_tokens() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[serial_test::serial(auth_refresh)]
|
||||
#[serial_test::serial(auth_env)]
|
||||
#[tokio::test]
|
||||
async fn unauthorized_recovery_errors_on_account_mismatch() -> Result<()> {
|
||||
skip_if_no_network!(Ok(()));
|
||||
@@ -1154,7 +1166,7 @@ async fn unauthorized_recovery_errors_on_account_mismatch() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[serial_test::serial(auth_refresh)]
|
||||
#[serial_test::serial(auth_env)]
|
||||
#[tokio::test]
|
||||
async fn unauthorized_recovery_requires_chatgpt_auth() -> Result<()> {
|
||||
skip_if_no_network!(Ok(()));
|
||||
|
||||
@@ -7,6 +7,7 @@ use codex_login::AuthDotJson;
|
||||
use codex_login::AuthKeyringBackendKind;
|
||||
use codex_login::AuthManager;
|
||||
use codex_login::CLIENT_ID;
|
||||
use codex_login::CLIENT_ID_OVERRIDE_ENV_VAR;
|
||||
use codex_login::CODEX_ACCESS_TOKEN_ENV_VAR;
|
||||
use codex_login::REVOKE_TOKEN_URL_OVERRIDE_ENV_VAR;
|
||||
use codex_login::logout_with_revoke;
|
||||
@@ -28,11 +29,12 @@ use wiremock::matchers::path;
|
||||
const ACCESS_TOKEN: &str = "access-token";
|
||||
const REFRESH_TOKEN: &str = "refresh-token";
|
||||
|
||||
#[serial_test::serial(logout_revoke)]
|
||||
#[serial_test::serial(auth_env)]
|
||||
#[tokio::test]
|
||||
async fn logout_with_revoke_revokes_refresh_token_then_removes_auth() -> Result<()> {
|
||||
skip_if_no_network!(Ok(()));
|
||||
|
||||
let _client_id_guard = EnvGuard::set(CLIENT_ID_OVERRIDE_ENV_VAR, "staging-client".to_string());
|
||||
let server = MockServer::start().await;
|
||||
Mock::given(method("POST"))
|
||||
.and(path("/oauth/revoke"))
|
||||
@@ -77,14 +79,14 @@ async fn logout_with_revoke_revokes_refresh_token_then_removes_auth() -> Result<
|
||||
json!({
|
||||
"token": REFRESH_TOKEN,
|
||||
"token_type_hint": "refresh_token",
|
||||
"client_id": CLIENT_ID,
|
||||
"client_id": "staging-client",
|
||||
})
|
||||
);
|
||||
server.verify().await;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[serial_test::serial(logout_revoke)]
|
||||
#[serial_test::serial(auth_env)]
|
||||
#[tokio::test]
|
||||
async fn logout_with_revoke_uses_stored_auth_when_access_token_env_is_set() -> Result<()> {
|
||||
skip_if_no_network!(Ok(()));
|
||||
@@ -126,7 +128,7 @@ async fn logout_with_revoke_uses_stored_auth_when_access_token_env_is_set() -> R
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[serial_test::serial(logout_revoke)]
|
||||
#[serial_test::serial(auth_env)]
|
||||
#[tokio::test]
|
||||
async fn logout_with_revoke_removes_auth_when_revoke_fails() -> Result<()> {
|
||||
skip_if_no_network!(Ok(()));
|
||||
@@ -169,7 +171,7 @@ async fn logout_with_revoke_removes_auth_when_revoke_fails() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[serial_test::serial(logout_revoke)]
|
||||
#[serial_test::serial(auth_env)]
|
||||
#[tokio::test]
|
||||
async fn auth_manager_logout_with_revoke_uses_cached_auth() -> Result<()> {
|
||||
skip_if_no_network!(Ok(()));
|
||||
|
||||
Reference in new issue
Block a user