fix(google): bound gemini oauth token expiry · openclaw/openclaw@ef0882e
steipete
·
2026-05-30
·
via Recent Commits to openclaw:main
| Original file line number | Diff line number | Diff line change |
|---|
@@ -951,6 +951,39 @@ describe("loginGeminiCliOAuth", () => {
|
951 | 951 | expect(result.expires).toBeLessThanOrEqual(beforeRefresh); |
952 | 952 | }); |
953 | 953 | |
| 954 | +it("keeps invalid clocks out of refreshed Gemini CLI credential expiry", async () => { |
| 955 | +mockSettingsExistsSync.mockReturnValue(true); |
| 956 | +mockSettingsReadFileSync.mockReturnValue( |
| 957 | +JSON.stringify({ |
| 958 | +security: { |
| 959 | +auth: { |
| 960 | +selectedType: "oauth-personal", |
| 961 | +}, |
| 962 | +}, |
| 963 | +}), |
| 964 | +); |
| 965 | + |
| 966 | +installGeminiOAuthFetchMock(() => undefined, { |
| 967 | +tokenResponse: () => |
| 968 | +responseJson({ |
| 969 | +access_token: "access-token", |
| 970 | +expires_in: 3600, |
| 971 | +}), |
| 972 | +}); |
| 973 | +const dateNow = vi.spyOn(Date, "now").mockReturnValue(Number.NaN); |
| 974 | +try { |
| 975 | +const { refreshTokensForGeminiCli } = await import("./oauth.token.js"); |
| 976 | +const result = await refreshTokensForGeminiCli({ |
| 977 | +refresh: "refresh-token", |
| 978 | +email: "lobster@openclaw.ai", |
| 979 | +}); |
| 980 | + |
| 981 | +expect(result.expires).toBe(0); |
| 982 | +} finally { |
| 983 | +dateNow.mockRestore(); |
| 984 | +} |
| 985 | +}); |
| 986 | + |
954 | 987 | it("keeps unsafe token expiry values out of refreshed Gemini CLI credentials", async () => { |
955 | 988 | mockSettingsExistsSync.mockReturnValue(true); |
956 | 989 | mockSettingsReadFileSync.mockReturnValue( |
|
| Original file line number | Diff line number | Diff line change |
|---|
|
1 | | -import { resolveExpiresAtMsFromDurationSeconds } from "openclaw/plugin-sdk/number-runtime"; |
| 1 | +import { |
| 2 | +asDateTimestampMs, |
| 3 | +resolveExpiresAtMsFromDurationSeconds, |
| 4 | +} from "openclaw/plugin-sdk/number-runtime"; |
2 | 5 | import { resolveOAuthClientConfig } from "./oauth.credentials.js"; |
3 | 6 | import { fetchWithTimeout } from "./oauth.http.js"; |
4 | 7 | import { resolveGoogleOAuthIdentity, resolveGooglePersonalOAuthIdentity } from "./oauth.project.js"; |
@@ -34,10 +37,18 @@ async function requestTokenGrant(body: URLSearchParams): Promise<{
|
34 | 37 | }; |
35 | 38 | } |
36 | 39 | |
| 40 | +function resolveExpiredTokenTimestampMs(nowMs: number): number { |
| 41 | +return asDateTimestampMs(nowMs - TOKEN_EXPIRY_BUFFER_MS) ?? nowMs; |
| 42 | +} |
| 43 | + |
37 | 44 | function resolveTokenExpiresAt(value: unknown): number { |
| 45 | +const nowMs = asDateTimestampMs(Date.now()); |
| 46 | +if (nowMs === undefined) { |
| 47 | +return 0; |
| 48 | +} |
38 | 49 | return ( |
39 | | -resolveExpiresAtMsFromDurationSeconds(value, { bufferMs: TOKEN_EXPIRY_BUFFER_MS }) ?? |
40 | | -Date.now() - TOKEN_EXPIRY_BUFFER_MS |
| 50 | +resolveExpiresAtMsFromDurationSeconds(value, { nowMs, bufferMs: TOKEN_EXPIRY_BUFFER_MS }) ?? |
| 51 | +resolveExpiredTokenTimestampMs(nowMs) |
41 | 52 | ); |
42 | 53 | } |
43 | 54 | |
|
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。