fix(oauth): 兼容 Kiro device token 的 snake_case 字段并保留 error 响应体

- device poll 同时接受 accessToken/access_token、refreshToken/refresh_token、expiresIn/expires_in
- post_kiro_device_oidc_json 在解析失败响应时保留完整 JSON 并附加 _error 标记, 避免丢失 authorization_pending 等详细信息
- 补充 authorization_pending 时 device session 保持 pending 状态的测试
This commit is contained in:
fawney19
2026-04-20 15:00:02 +08:00
parent 078347b722
commit ffe5d16094
3 changed files with 114 additions and 9 deletions

View File

@@ -217,7 +217,11 @@ pub(super) async fn handle_admin_provider_oauth_device_poll(
.into_response());
}
let Some(access_token) = json_non_empty_string(token_result.get("accessToken")) else {
let Some(access_token) = json_non_empty_string(
token_result
.get("accessToken")
.or_else(|| token_result.get("access_token")),
) else {
return Ok(Json(json!({
"status": "error",
"error": "token 响应缺少 accessToken 或 refreshToken",
@@ -225,7 +229,11 @@ pub(super) async fn handle_admin_provider_oauth_device_poll(
}))
.into_response());
};
let Some(refresh_token) = json_non_empty_string(token_result.get("refreshToken")) else {
let Some(refresh_token) = json_non_empty_string(
token_result
.get("refreshToken")
.or_else(|| token_result.get("refresh_token")),
) else {
return Ok(Json(json!({
"status": "error",
"error": "token 响应缺少 accessToken 或 refreshToken",
@@ -233,9 +241,13 @@ pub(super) async fn handle_admin_provider_oauth_device_poll(
}))
.into_response());
};
let initial_expires_at = json_u64_value(token_result.get("expiresIn"))
.map(|expires_in| current_unix_secs().saturating_add(expires_in))
.unwrap_or_else(|| current_unix_secs().saturating_add(3600));
let initial_expires_at = json_u64_value(
token_result
.get("expiresIn")
.or_else(|| token_result.get("expires_in")),
)
.map(|expires_in| current_unix_secs().saturating_add(expires_in))
.unwrap_or_else(|| current_unix_secs().saturating_add(3600));
let social_refresh_base_url =
admin_provider_oauth_kiro_refresh_base_url_override(state, "kiro_social_refresh");
let idc_refresh_base_url =

View File

@@ -578,12 +578,25 @@ async fn post_kiro_device_oidc_json(
let status = response.status;
let body_text = response.body_text;
Ok(
serde_json::from_str::<serde_json::Value>(&body_text).unwrap_or_else(|_| {
json!({
match serde_json::from_str::<serde_json::Value>(&body_text) {
Ok(mut payload) => {
if !status.is_success() {
if let Some(object) = payload.as_object_mut() {
object.insert("_error".to_string(), json!(true));
} else {
payload = json!({
"_error": true,
"data": payload,
});
}
}
payload
}
Err(_) => json!({
"_error": !status.is_success(),
"error": body_text.trim(),
})
}),
}),
},
)
}