Refresh ChatGPT auth token (#2484)

ChatGPT token's live for only 1 hour. If the session is longer we don't
refresh the token. We should get the expiry timestamp and attempt to
refresh before it.
This commit is contained in:
Ahmed Ibrahim
2025-08-19 21:01:31 -07:00
committed by GitHub
parent eaae56a1b0
commit d1f1e36836
2 changed files with 41 additions and 0 deletions

View File

@@ -252,6 +252,14 @@ impl ModelClient {
.and_then(|v| v.to_str().ok())
.and_then(|s| s.parse::<u64>().ok());
if status == StatusCode::UNAUTHORIZED {
if let Some(a) = auth.as_ref() {
let _ = a.refresh_token().await;
}
// Retry immediately with refreshed credentials.
continue;
}
// The OpenAI Responses endpoint returns structured JSON bodies even for 4xx/5xx
// errors. When we bubble early with only the HTTP status the caller sees an opaque
// "unexpected status 400 Bad Request" which makes debugging nearly impossible.

View File

@@ -62,6 +62,39 @@ impl CodexAuth {
}
}
pub async fn refresh_token(&self) -> Result<String, std::io::Error> {
let token_data = self
.get_current_token_data()
.ok_or(std::io::Error::other("Token data is not available."))?;
let token = token_data.refresh_token;
let refresh_response = try_refresh_token(token)
.await
.map_err(std::io::Error::other)?;
let updated = update_tokens(
&self.auth_file,
refresh_response.id_token,
refresh_response.access_token,
refresh_response.refresh_token,
)
.await?;
if let Ok(mut auth_lock) = self.auth_dot_json.lock() {
*auth_lock = Some(updated.clone());
}
let access = match updated.tokens {
Some(t) => t.access_token,
None => {
return Err(std::io::Error::other(
"Token data is not available after refresh.",
));
}
};
Ok(access)
}
/// Loads the available auth information from the auth.json or
/// OPENAI_API_KEY environment variable.
pub fn from_codex_home(