mirror of
https://github.com/hanzoai/dev.git
synced 2026-10-25 03:32:32 +00:00
fix(login): fallback to /oauth2/token and send Accept header for token exchange\n\nSome environments report failures posting to https://auth.openai.com/oauth/token. Try both /oauth/token and /oauth2/token, include Accept: application/json, and surface clearer error messages.\n\nThis improves compatibility with recent issuer changes and should resolve token exchange failures.\n\nCloses #21
This commit is contained in:
@@ -326,26 +326,36 @@ async fn exchange_code_for_tokens(
|
|||||||
}
|
}
|
||||||
|
|
||||||
let client = codex_core::http_client::build_http_client();
|
let client = codex_core::http_client::build_http_client();
|
||||||
let resp = client
|
let body = format!(
|
||||||
.post(format!("{issuer}/oauth/token"))
|
"grant_type=authorization_code&code={}&redirect_uri={}&client_id={}&code_verifier={}",
|
||||||
.header("Content-Type", "application/x-www-form-urlencoded")
|
urlencoding::encode(code),
|
||||||
.body(format!(
|
urlencoding::encode(redirect_uri),
|
||||||
"grant_type=authorization_code&code={}&redirect_uri={}&client_id={}&code_verifier={}",
|
urlencoding::encode(client_id),
|
||||||
urlencoding::encode(code),
|
urlencoding::encode(&pkce.code_verifier)
|
||||||
urlencoding::encode(redirect_uri),
|
);
|
||||||
urlencoding::encode(client_id),
|
|
||||||
urlencoding::encode(&pkce.code_verifier)
|
|
||||||
))
|
|
||||||
.send()
|
|
||||||
.await
|
|
||||||
.map_err(io::Error::other)?;
|
|
||||||
|
|
||||||
if !resp.status().is_success() {
|
// Try legacy endpoint first, then fallback to oauth2 path used by some issuers
|
||||||
return Err(io::Error::other(format!(
|
let mut last_status: Option<reqwest::StatusCode> = None;
|
||||||
"token endpoint returned status {}",
|
let mut resp_opt = None;
|
||||||
resp.status()
|
for path in ["/oauth/token", "/oauth2/token"] {
|
||||||
)));
|
let url = format!("{issuer}{path}");
|
||||||
|
let res = client
|
||||||
|
.post(&url)
|
||||||
|
.header("Content-Type", "application/x-www-form-urlencoded")
|
||||||
|
.header("Accept", "application/json")
|
||||||
|
.body(body.clone())
|
||||||
|
.send()
|
||||||
|
.await;
|
||||||
|
match res {
|
||||||
|
Ok(resp) if resp.status().is_success() => { resp_opt = Some(resp); break; }
|
||||||
|
Ok(resp) => { last_status = Some(resp.status()); }
|
||||||
|
Err(e) => { return Err(io::Error::other(format!("failed to POST {}: {}", url, e))); }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
let resp = resp_opt.ok_or_else(|| io::Error::other(format!(
|
||||||
|
"token endpoint failed (last status {:?})",
|
||||||
|
last_status
|
||||||
|
)))?;
|
||||||
|
|
||||||
let tokens: TokenResponse = resp.json().await.map_err(io::Error::other)?;
|
let tokens: TokenResponse = resp.json().await.map_err(io::Error::other)?;
|
||||||
Ok(ExchangedTokens {
|
Ok(ExchangedTokens {
|
||||||
@@ -495,26 +505,35 @@ async fn obtain_api_key(issuer: &str, client_id: &str, id_token: &str) -> io::Re
|
|||||||
access_token: String,
|
access_token: String,
|
||||||
}
|
}
|
||||||
let client = codex_core::http_client::build_http_client();
|
let client = codex_core::http_client::build_http_client();
|
||||||
let resp = client
|
let form = format!(
|
||||||
.post(format!("{issuer}/oauth/token"))
|
"grant_type={}&client_id={}&requested_token={}&subject_token={}&subject_token_type={}",
|
||||||
.header("Content-Type", "application/x-www-form-urlencoded")
|
urlencoding::encode("urn:ietf:params:oauth:grant-type:token-exchange"),
|
||||||
.body(format!(
|
urlencoding::encode(client_id),
|
||||||
"grant_type={}&client_id={}&requested_token={}&subject_token={}&subject_token_type={}",
|
urlencoding::encode("openai-api-key"),
|
||||||
urlencoding::encode("urn:ietf:params:oauth:grant-type:token-exchange"),
|
urlencoding::encode(id_token),
|
||||||
urlencoding::encode(client_id),
|
urlencoding::encode("urn:ietf:params:oauth:token-type:id_token")
|
||||||
urlencoding::encode("openai-api-key"),
|
);
|
||||||
urlencoding::encode(id_token),
|
let mut last_status: Option<reqwest::StatusCode> = None;
|
||||||
urlencoding::encode("urn:ietf:params:oauth:token-type:id_token")
|
let mut resp_opt = None;
|
||||||
))
|
for path in ["/oauth/token", "/oauth2/token"] {
|
||||||
.send()
|
let url = format!("{issuer}{path}");
|
||||||
.await
|
let res = client
|
||||||
.map_err(io::Error::other)?;
|
.post(&url)
|
||||||
if !resp.status().is_success() {
|
.header("Content-Type", "application/x-www-form-urlencoded")
|
||||||
return Err(io::Error::other(format!(
|
.header("Accept", "application/json")
|
||||||
"api key exchange failed with status {}",
|
.body(form.clone())
|
||||||
resp.status()
|
.send()
|
||||||
)));
|
.await;
|
||||||
|
match res {
|
||||||
|
Ok(resp) if resp.status().is_success() => { resp_opt = Some(resp); break; }
|
||||||
|
Ok(resp) => { last_status = Some(resp.status()); }
|
||||||
|
Err(e) => { return Err(io::Error::other(format!("failed to POST {}: {}", url, e))); }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
let resp = resp_opt.ok_or_else(|| io::Error::other(format!(
|
||||||
|
"api key exchange failed (last status {:?})",
|
||||||
|
last_status
|
||||||
|
)))?;
|
||||||
let body: ExchangeResp = resp.json().await.map_err(io::Error::other)?;
|
let body: ExchangeResp = resp.json().await.map_err(io::Error::other)?;
|
||||||
Ok(body.access_token)
|
Ok(body.access_token)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user