From 3bbf014cbe33527ae4e920fff9bdd3efe15e0af5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=90=BD=E5=B0=8F=E9=9B=A8?= <181683471+a137460387@users.noreply.github.com> Date: Thu, 5 Nov 2026 12:56:30 +0800 Subject: [PATCH] =?UTF-8?q?1.=20=E4=BF=AE=E5=A4=8D=EF=BC=9A=E5=87=AD?= =?UTF-8?q?=E8=AF=81=E5=88=B7=E6=96=B0=E3=80=8C=E6=B2=A1=E5=86=99=E5=9B=9E?= =?UTF-8?q?=E3=80=8D=E8=A2=AB=E4=B8=80=E5=BE=8B=E5=88=A4=E6=88=90=E5=A4=B1?= =?UTF-8?q?=E8=B4=A5=20=E2=80=94=E2=80=94=20issue=20#407=20=E7=9A=84?= =?UTF-8?q?=E7=BB=93=E6=9E=9C=E6=A0=B8=E9=AA=8C=E5=9C=A8=E5=88=B7=E6=96=B0?= =?UTF-8?q?=E5=89=8D=E5=90=8E=E5=90=84=E5=8F=96=E4=B8=80=E6=AC=A1=20`(expi?= =?UTF-8?q?resAt,=20updatedAt)`=EF=BC=8C=E6=8C=87=E7=BA=B9=E6=B2=A1?= =?UTF-8?q?=E6=8E=A8=E8=BF=9B=E5=B0=B1=E6=8A=A5=E3=80=8C=E9=80=82=E9=85=8D?= =?UTF-8?q?=E5=99=A8=E6=8A=A5=E6=88=90=E5=8A=9F=E4=BD=86=E5=87=AD=E6=8D=AE?= =?UTF-8?q?=E6=9C=AA=E6=9B=B4=E6=96=B0=EF=BC=8C=E8=AF=B7=E9=87=8D=E6=96=B0?= =?UTF-8?q?=E7=99=BB=E5=BD=95=E8=AF=A5=E8=B4=A6=E5=8F=B7=E3=80=8D=E3=80=82?= =?UTF-8?q?=E4=BD=86=E6=9C=89=E4=B8=80=E7=B1=BB**=E6=AD=A3=E5=B8=B8**?= =?UTF-8?q?=E6=83=85=E5=BD=A2=E4=BC=9A=E8=90=BD=E8=BF=9B=E8=BF=99=E6=9D=A1?= =?UTF-8?q?=E5=88=A4=E6=8D=AE=EF=BC=9A=E4=B8=8A=E6=B8=B8=E5=9C=A8=20access?= =?UTF-8?q?=20token=20=E4=BB=8D=E5=9C=A8=E6=9C=89=E6=95=88=E6=9C=9F?= =?UTF-8?q?=E5=86=85=E6=97=B6=E5=8E=9F=E6=A0=B7=E5=9B=9E=E5=90=8C=E4=B8=80?= =?UTF-8?q?=E4=BB=BD=E5=87=AD=E8=AF=81=EF=BC=88AutoClaw=20=E7=9A=84=20`/us?= =?UTF-8?q?erapi/v1/refresh`=20=E5=B0=B1=E6=98=AF=E8=BF=99=E6=A0=B7=20?= =?UTF-8?q?=E2=80=94=E2=80=94=20=E5=AE=83=E7=BB=99=E7=9A=84=E6=98=AF?= =?UTF-8?q?=E3=80=8C=E4=B8=80=E4=BB=BD=E8=83=BD=E7=94=A8=E7=9A=84=E3=80=8D?= =?UTF-8?q?=EF=BC=8C=E4=B8=8D=E6=98=AF=E3=80=8C=E6=8D=A2=E4=B8=80=E4=BB=BD?= =?UTF-8?q?=E6=96=B0=E7=9A=84=E3=80=8D=EF=BC=89=EF=BC=8C=E6=8F=92=E4=BB=B6?= =?UTF-8?q?=E6=8C=89=E8=AE=BE=E8=AE=A1=E3=80=8C=E6=B2=A1=E5=8F=98=E5=8C=96?= =?UTF-8?q?=E5=B0=B1=E4=B8=8D=E8=90=BD=E7=9B=98=E3=80=8D=EF=BC=8C=E8=AE=B0?= =?UTF-8?q?=E5=BD=95=E4=B8=80=E4=B8=AA=E5=AD=97=E8=8A=82=E4=B8=8D=E5=8A=A8?= =?UTF-8?q?=E3=80=82=E7=94=A8=E6=88=B7=E7=82=B9=E3=80=8C=E5=88=B7=E6=96=B0?= =?UTF-8?q?=20token=E3=80=8D=E5=BE=97=E5=88=B0=E7=9A=84=E6=98=AF=E4=B8=80?= =?UTF-8?q?=E5=8F=A5=E6=8A=8A=E4=BA=BA=E5=BE=80=E9=87=8D=E6=96=B0=E7=99=BB?= =?UTF-8?q?=E5=BD=95=E5=B8=A6=E7=9A=84=E8=AF=9D=EF=BC=8C=E8=80=8C=E5=85=B6?= =?UTF-8?q?=E5=AE=9E=E4=BB=80=E4=B9=88=E9=83=BD=E4=B8=8D=E7=94=A8=E5=81=9A?= =?UTF-8?q?=E3=80=82=E5=AE=9E=E6=B5=8B=EF=BC=88v3.4.1=20+=20=E6=8F=92?= =?UTF-8?q?=E4=BB=B6=203.4.1=EF=BC=89=EF=BC=9A=E6=9C=AA=E4=B8=B4=E6=9C=9F?= =?UTF-8?q?=E8=B4=A6=E5=8F=B7=E7=82=B9=E5=88=B7=E6=96=B0=E7=A8=B3=E5=AE=9A?= =?UTF-8?q?=20502=E3=80=82=202.=20=E4=BC=98=E5=8C=96=EF=BC=9A=E6=B2=A1?= =?UTF-8?q?=E5=86=99=E5=9B=9E=E6=97=B6=E5=85=88=E5=A4=8D=E6=9F=A5=E4=B8=80?= =?UTF-8?q?=E6=AC=A1=E9=80=82=E9=85=8D=E5=99=A8=E7=9A=84=E4=B8=B4=E6=9C=9F?= =?UTF-8?q?=E5=88=A4=E5=AE=9A=20=E2=80=94=E2=80=94=20`credentials=5Fexpiri?= =?UTF-8?q?ng=5Fasync(store,=20id)`=20=E4=B8=BA=20false=EF=BC=88=E5=B7=B2?= =?UTF-8?q?=E4=B8=8D=E5=9C=A8=E4=B8=B4=E6=9C=9F=E7=AA=97=E5=8F=A3=EF=BC=89?= =?UTF-8?q?=E5=B0=B1=E8=A7=86=E4=B8=BA=E3=80=8C=E6=97=A0=E9=9C=80=E5=88=B7?= =?UTF-8?q?=E6=96=B0=E3=80=8D=E6=AD=A3=E5=B8=B8=E8=BF=94=E5=9B=9E=EF=BC=8C?= =?UTF-8?q?=E5=8F=AA=E7=95=99=E4=B8=80=E6=9D=A1=20verbose=20=E6=97=A5?= =?UTF-8?q?=E5=BF=97=EF=BC=9B=E4=BB=8D=E4=B8=B4=E6=9C=9F=20/=20=E5=B7=B2?= =?UTF-8?q?=E8=BF=87=E6=9C=9F=E5=8D=B4=E6=B2=A1=E6=8D=A2=E5=8F=91=E6=89=8D?= =?UTF-8?q?=E6=98=AF=E7=9C=9F=E6=95=85=E9=9A=9C=EF=BC=8C=E7=BB=A7=E7=BB=AD?= =?UTF-8?q?=E6=8A=A5=E9=94=99=E5=B9=B6=E6=8F=90=E7=A4=BA=E9=87=8D=E6=96=B0?= =?UTF-8?q?=E7=99=BB=E5=BD=95=E3=80=82=E5=A4=8D=E6=9F=A5=E6=94=BE=E5=9C=A8?= =?UTF-8?q?=20`refresh=5Faccess=5Ftoken`=20=E4=B9=8B=E5=90=8E=E5=8F=96?= =?UTF-8?q?=EF=BC=8C=E5=88=A4=E6=8D=AE=E6=98=AF=E5=88=B7=E6=96=B0=E7=BB=93?= =?UTF-8?q?=E6=9E=9C=E8=90=BD=E7=9B=98=E5=90=8E=E7=9A=84=E7=8E=B0=E7=8A=B6?= =?UTF-8?q?=EF=BC=8C=E4=B8=8D=E6=98=AF=E8=B0=83=E7=94=A8=E5=89=8D=E7=9A=84?= =?UTF-8?q?=E5=BF=AB=E7=85=A7=E3=80=82=203.=20=E8=AF=B4=E6=98=8E=EF=BC=9A?= =?UTF-8?q?=E6=89=8B=E5=8A=A8=E5=88=B7=E6=96=B0=EF=BC=88`POST=20/api/accou?= =?UTF-8?q?nts/refresh`=EF=BC=89=E4=B8=8E=E8=87=AA=E5=8A=A8=E7=BB=B4?= =?UTF-8?q?=E6=8A=A4=E5=85=B1=E7=94=A8=E6=9C=AC=E5=87=BD=E6=95=B0=EF=BC=8C?= =?UTF-8?q?=E4=B8=A4=E5=A4=84=E5=8F=A3=E5=BE=84=E5=90=8C=E6=97=B6=E7=94=9F?= =?UTF-8?q?=E6=95=88=E3=80=82=E5=AE=9E=E6=B5=8B=EF=BC=9A=E6=9C=AA=E4=B8=B4?= =?UTF-8?q?=E6=9C=9F=E8=B4=A6=E5=8F=B7=E5=88=B7=E6=96=B0=E7=94=B1=20502=20?= =?UTF-8?q?=E5=8F=98=E4=B8=BA=20200=EF=BC=8C=E6=97=A5=E5=BF=97=E5=87=BA?= =?UTF-8?q?=E7=8E=B0=E3=80=8C=E5=88=B7=E6=96=B0=E6=9C=AA=E6=8D=A2=E5=8F=91?= =?UTF-8?q?=E6=96=B0=E5=87=AD=E8=AF=81=EF=BC=8C=E4=BD=86=E5=BD=93=E5=89=8D?= =?UTF-8?q?=E4=BB=8D=E5=9C=A8=E6=9C=89=E6=95=88=E6=9C=9F=E5=86=85=20?= =?UTF-8?q?=E2=80=94=E2=80=94=20=E8=A7=86=E4=B8=BA=E6=97=A0=E9=9C=80?= =?UTF-8?q?=E5=88=B7=E6=96=B0=E3=80=8D=EF=BC=9B=E5=90=8C=E4=B8=80=E6=89=B9?= =?UTF-8?q?=E9=87=8C=E5=87=AD=E8=AF=81=E5=B7=B2=E8=BF=87=E6=9C=9F=E3=80=81?= =?UTF-8?q?=E4=B8=8A=E6=B8=B8=E5=8F=88=E7=A1=AE=E5=AE=9E=E4=B8=8D=E6=8D=A2?= =?UTF-8?q?=E5=8F=91=E7=9A=84=E8=B4=A6=E5=8F=B7=E4=BB=8D=E7=84=B6=E7=85=A7?= =?UTF-8?q?=E6=97=A7=E6=8A=A5=E9=94=99=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/server/core/credential_maintenance.rs | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/desktop-tauri/src-tauri/server/src/server/core/credential_maintenance.rs b/desktop-tauri/src-tauri/server/src/server/core/credential_maintenance.rs index 983d5fe49..00bbc0f06 100644 --- a/desktop-tauri/src-tauri/server/src/server/core/credential_maintenance.rs +++ b/desktop-tauri/src-tauri/server/src/server/core/credential_maintenance.rs @@ -96,6 +96,18 @@ pub const STATUS_FAILED: &str = "failed"; /// `expiresAt` 或 `updatedAt` **任一**推进都算成功:有些家刷新后只换 token /// 不延长有效期(反过来也有),单看一个字段会误判。账号读不到时放行 —— /// 那是「记录不存在」的问题,由刷新本身报错,不该在这里编一个。 +/// +/// ── 「没写回」不等于失败:先复查一次还临不临期 ──────────────── +/// 原判据(#407)把所有「指纹没推进」都记成失败,于是一类**正常**情形被打成 +/// 红字:上游在 access token **仍在有效期内**时,刷新接口会原样回同一份凭证 +/// (实测 AutoClaw 的 `/userapi/v1/refresh` 就是这样 —— 它给的是「一份能用的」, +/// 不是「换一份新的」)。插件按设计「没变化就不落盘」,记录一个字节不动, +/// 这里就报「适配器报成功但凭据未更新,请重新登录该账号」—— 用户点「刷新 token」 +/// 得到的是一句把人往重新登录带的话,而其实什么都不用做。 +/// +/// 因此没写回时**再问一次适配器**:凭证已经不在临期窗口里(`false`)就视为 +/// 「无需刷新」正常返回;只有「确实临期/已过期、却还是没换发」才是真故障 +/// (上游把这份登录态作废了),那时报错并提示重新登录才是对的。 pub async fn refresh_checked( store: &AccountStore, adapter: &dyn ProviderAdapter, @@ -108,6 +120,15 @@ pub async fn refresh_checked( (before, after) { if expires_after <= expires_before && updated_after <= updated_before { + // 复查必须在 `refresh_access_token` **之后**做:判断依据是刷新结果 + // 落在记录上的现状,不是调用前的快照。 + if !adapter.credentials_expiring_async(store, id).await { + logging::verbose( + "[Accounts]", + &format!("账号 {id} 的刷新未换发新凭证,但当前仍在有效期内 —— 视为无需刷新"), + ); + return Ok(token); + } return Err(GatewayError::with_status( 502, format!(