feat: Implemented OAuth support for OpenAI models via Codex endpoints
CI / All (ubuntu-latest) (push) Failing after 24s
CI / All (macos-latest) (push) Has been cancelled
CI / All (windows-latest) (push) Has been cancelled

This commit is contained in:
2026-07-06 11:20:32 -06:00
parent af91b89cff
commit fa7eadd08a
7 changed files with 477 additions and 44 deletions
+79
View File
@@ -0,0 +1,79 @@
use super::oauth::{OAuthProvider, TokenRequestFormat};
use base64::Engine;
use base64::engine::general_purpose::URL_SAFE_NO_PAD;
use serde_json::Value;
pub struct OpenAIOAuthProvider;
impl OAuthProvider for OpenAIOAuthProvider {
fn provider_name(&self) -> &str {
"openai"
}
fn client_id(&self) -> &str {
"app_EMoamEEZ73f0CkXaXp7hrann"
}
fn authorize_url(&self) -> &str {
"https://auth.openai.com/oauth/authorize"
}
fn token_url(&self) -> &str {
"https://auth.openai.com/oauth/token"
}
fn redirect_uri(&self) -> &str {
"http://localhost:1455/auth/callback"
}
fn scopes(&self) -> &str {
"openid profile email offline_access"
}
fn token_request_format(&self) -> TokenRequestFormat {
TokenRequestFormat::FormUrlEncoded
}
fn extra_authorize_params(&self) -> Vec<(&str, &str)> {
vec![
("id_token_add_organizations", "true"),
("codex_cli_simplified_flow", "true"),
]
}
fn fixed_redirect_uri(&self) -> Option<String> {
Some("http://localhost:1455/auth/callback".to_string())
}
fn include_state_in_token_exchange(&self) -> bool {
false
}
fn extract_account_id(&self, response: &Value) -> Option<String> {
let id_token = response["id_token"].as_str().unwrap_or_default();
let access_token = response["access_token"].as_str().unwrap_or_default();
extract_account_id_from_jwt(id_token).or_else(|| extract_account_id_from_jwt(access_token))
}
}
fn extract_account_id_from_jwt(token: &str) -> Option<String> {
let parts: Vec<&str> = token.splitn(3, '.').collect();
if parts.len() < 2 {
return None;
}
let decoded = URL_SAFE_NO_PAD.decode(parts[1]).ok()?;
let claims: Value = serde_json::from_slice(&decoded).ok()?;
claims["chatgpt_account_id"]
.as_str()
.map(|s| s.to_string())
.or_else(|| {
claims["https://api.openai.com/auth"]["chatgpt_account_id"]
.as_str()
.map(|s| s.to_string())
})
.or_else(|| {
claims["organizations"][0]["id"]
.as_str()
.map(|s| s.to_string())
})
}