Files
Aether/apps/aether-gateway/src/orchestration/oauth_error.rs
T
elky 531cf11025 feat(gateway): harden provider request execution
Preserve exact request payloads and model client surface and API operation explicitly.

Add Anthropic compatibility profiles, bounded stream commitment, and scoped OAuth retry behavior across provider transports.
2026-07-27 09:36:31 +08:00

114 lines
3.6 KiB
Rust

pub(crate) fn oauth_status_may_be_invalid(status_code: u16, response_text: Option<&str>) -> bool {
if status_code == 401 {
return true;
}
if status_code != 403 {
return false;
}
let Some(response_text) = response_text else {
return false;
};
if let Ok(body) = serde_json::from_str::<serde_json::Value>(response_text) {
let error_type = body
.get("error")
.and_then(|error| error.get("type"))
.and_then(serde_json::Value::as_str)
.map(str::trim)
.filter(|value| !value.is_empty())
.or_else(|| {
body.get("type")
.and_then(serde_json::Value::as_str)
.map(str::trim)
.filter(|value| !value.is_empty() && !value.eq_ignore_ascii_case("error"))
});
if let Some(error_type) = error_type {
return is_oauth_invalid_error_taxonomy(error_type);
}
let error_code = body
.get("error")
.and_then(|error| error.get("code"))
.or_else(|| body.get("code"))
.or_else(|| body.get("error").filter(|error| error.is_string()))
.and_then(serde_json::Value::as_str)
.map(str::trim)
.filter(|value| !value.is_empty());
if error_code.is_some_and(is_oauth_invalid_error_taxonomy) {
return true;
}
return response_has_oauth_invalid_phrase(response_text);
}
response_has_oauth_invalid_phrase(response_text)
}
pub(crate) fn oauth_status_proves_access_token_invalid(
status_code: u16,
response_text: Option<&str>,
) -> bool {
if status_code == 401 {
return true;
}
if status_code != 403 {
return false;
}
response_text.is_some_and(response_has_oauth_invalid_phrase)
}
fn is_oauth_invalid_error_taxonomy(value: &str) -> bool {
matches!(
value.trim().to_ascii_lowercase().as_str(),
"authentication_error"
| "invalid_authentication_token"
| "invalid_token"
| "oauth_token_invalid"
| "token_invalid"
| "token_expired"
| "unauthenticated"
| "biscuit_baker_service_auth_credential_error_status"
)
}
fn response_has_oauth_invalid_phrase(response_text: &str) -> bool {
let response_text = response_text.to_ascii_lowercase();
if [
"oauth_token_invalid",
"invalid_token",
"biscuit_baker_service_auth_credential_error_status",
]
.iter()
.any(|taxonomy| contains_ascii_taxonomy_token(&response_text, taxonomy))
{
return true;
}
[
"oauth token is invalid",
"oauth token is expired",
"oauth token has expired",
"invalid access token",
"access token invalid",
"access token expired",
"expired access token",
"authentication token has been invalidated",
"token has been invalidated",
"personal access token owner is inactive",
"security token included in the request is expired",
]
.iter()
.any(|needle| response_text.contains(needle))
}
fn contains_ascii_taxonomy_token(text: &str, taxonomy: &str) -> bool {
text.match_indices(taxonomy).any(|(start, matched)| {
let end = start + matched.len();
let is_identifier_byte = |byte: u8| byte.is_ascii_alphanumeric() || byte == b'_';
let has_left_boundary = start == 0 || !is_identifier_byte(text.as_bytes()[start - 1]);
let has_right_boundary = end == text.len() || !is_identifier_byte(text.as_bytes()[end]);
has_left_boundary && has_right_boundary
})
}