codex: narrow API provisioning options

Co-authored-by: Codex <noreply@openai.com>
This commit is contained in:
Michael Fan
2026-03-26 16:57:49 -04:00
co-authored by Codex
parent dd93a89cd2
commit dfa9c641b6
4 changed files with 36 additions and 66 deletions
+31 -58
View File
@@ -15,47 +15,30 @@ use crate::pkce::PkceCodes;
const AUTH_ISSUER: &str = "https://auth.openai.com";
const PLATFORM_HYDRA_CLIENT_ID: &str = "app_2SKx67EdpoN0G6j64rFvigXD";
const PLATFORM_AUDIENCE: &str = "https://api.openai.com/v1";
const DEFAULT_API_BASE: &str = "https://api.openai.com";
const DEFAULT_CALLBACK_PORT: u16 = 5000;
const API_BASE: &str = "https://api.openai.com";
const CALLBACK_PORT: u16 = 0;
const CALLBACK_PATH: &str = "/auth/callback";
const DEFAULT_SCOPE: &str = "openid email profile offline_access";
const DEFAULT_APP: &str = "api";
const SCOPE: &str = "openid email profile offline_access";
const APP: &str = "api";
const USER_AGENT: &str = "Codex-API-Provision/1.0";
const DEFAULT_PROJECT_API_KEY_NAME: &str = "Codex CLI";
const DEFAULT_PROJECT_POLL_INTERVAL_SECONDS: u64 = 10;
const DEFAULT_PROJECT_POLL_TIMEOUT_SECONDS: u64 = 60;
const PROJECT_API_KEY_NAME: &str = "Codex CLI";
const PROJECT_POLL_INTERVAL_SECONDS: u64 = 10;
const PROJECT_POLL_TIMEOUT_SECONDS: u64 = 60;
const OAUTH_TIMEOUT_SECONDS: u64 = 15 * 60;
const HTTP_TIMEOUT_SECONDS: u64 = 30;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ApiProvisionOptions {
pub issuer: String,
pub client_id: String,
pub audience: String,
pub api_base: String,
pub app: String,
pub callback_port: u16,
pub scope: String,
pub api_key_name: String,
pub project_poll_interval_seconds: u64,
pub project_poll_timeout_seconds: u64,
}
impl Default for ApiProvisionOptions {
fn default() -> Self {
Self {
issuer: AUTH_ISSUER.to_string(),
client_id: PLATFORM_HYDRA_CLIENT_ID.to_string(),
audience: PLATFORM_AUDIENCE.to_string(),
api_base: DEFAULT_API_BASE.to_string(),
app: DEFAULT_APP.to_string(),
callback_port: DEFAULT_CALLBACK_PORT,
scope: DEFAULT_SCOPE.to_string(),
api_key_name: DEFAULT_PROJECT_API_KEY_NAME.to_string(),
project_poll_interval_seconds: DEFAULT_PROJECT_POLL_INTERVAL_SECONDS,
project_poll_timeout_seconds: DEFAULT_PROJECT_POLL_TIMEOUT_SECONDS,
}
}
struct ApiProvisionOptions {
issuer: String,
client_id: String,
audience: String,
api_base: String,
app: String,
callback_port: u16,
scope: String,
api_key_name: String,
project_poll_interval_seconds: u64,
project_poll_timeout_seconds: u64,
}
pub struct PendingApiProvisioning {
@@ -105,10 +88,19 @@ pub struct ProvisionedApiKey {
pub project_api_key: String,
}
pub fn start_api_provisioning(
options: ApiProvisionOptions,
) -> Result<PendingApiProvisioning, ApiProvisionError> {
validate_api_provision_options(&options)?;
pub fn start_api_provisioning() -> Result<PendingApiProvisioning, ApiProvisionError> {
let options = ApiProvisionOptions {
issuer: AUTH_ISSUER.to_string(),
client_id: PLATFORM_HYDRA_CLIENT_ID.to_string(),
audience: PLATFORM_AUDIENCE.to_string(),
api_base: API_BASE.to_string(),
app: APP.to_string(),
callback_port: CALLBACK_PORT,
scope: SCOPE.to_string(),
api_key_name: PROJECT_API_KEY_NAME.to_string(),
project_poll_interval_seconds: PROJECT_POLL_INTERVAL_SECONDS,
project_poll_timeout_seconds: PROJECT_POLL_TIMEOUT_SECONDS,
};
let client = build_http_client()?;
let callback_server = start_authorization_code_server(
options.callback_port,
@@ -131,25 +123,6 @@ pub fn start_api_provisioning(
})
}
fn validate_api_provision_options(options: &ApiProvisionOptions) -> Result<(), ApiProvisionError> {
if options.project_poll_interval_seconds == 0 {
return Err(ApiProvisionError::message(
"project_poll_interval_seconds must be greater than 0.".to_string(),
));
}
if options.project_poll_timeout_seconds == 0 {
return Err(ApiProvisionError::message(
"project_poll_timeout_seconds must be greater than 0.".to_string(),
));
}
if options.api_key_name.trim().is_empty() {
return Err(ApiProvisionError::message(
"api_key_name must not be empty.".to_string(),
));
}
Ok(())
}
fn build_authorize_url(
options: &ApiProvisionOptions,
redirect_uri: &str,
+4 -4
View File
@@ -132,10 +132,10 @@ async fn provision_from_authorization_code_provisions_api_key() {
client_id: "client-123".to_string(),
audience: PLATFORM_AUDIENCE.to_string(),
api_base: server.uri(),
app: DEFAULT_APP.to_string(),
callback_port: DEFAULT_CALLBACK_PORT,
scope: DEFAULT_SCOPE.to_string(),
api_key_name: DEFAULT_PROJECT_API_KEY_NAME.to_string(),
app: APP.to_string(),
callback_port: CALLBACK_PORT,
scope: SCOPE.to_string(),
api_key_name: PROJECT_API_KEY_NAME.to_string(),
project_poll_interval_seconds: 1,
project_poll_timeout_seconds: 5,
};
-1
View File
@@ -18,7 +18,6 @@ pub use server::ServerOptions;
pub use server::run_login_server;
pub use api_provision::ApiProvisionError;
pub use api_provision::ApiProvisionOptions;
pub use api_provision::PendingApiProvisioning;
pub use api_provision::ProvisionedApiKey;
pub use api_provision::start_api_provisioning;
+1 -3
View File
@@ -6,7 +6,6 @@ use codex_core::AuthManager;
use codex_core::auth::AuthCredentialsStoreMode;
use codex_core::auth::login_with_api_key;
use codex_core::auth::read_openai_api_key_from_env;
use codex_login::ApiProvisionOptions;
use codex_login::OPENAI_API_KEY_ENV_VAR;
use codex_login::PendingApiProvisioning;
use codex_login::ProvisionedApiKey;
@@ -62,8 +61,7 @@ fn start_api_provision(
)
})?;
let options = ApiProvisionOptions::default();
let session = start_api_provisioning(options)
let session = start_api_provisioning()
.map_err(|err| format!("Failed to start API provisioning: {err}"))?;
let browser_opened = session.open_browser();
let start_message = continue_in_browser_message(