From b28ac90364c5db9db7577c7fc3292a178b51e7b3 Mon Sep 17 00:00:00 2001 From: wucm667 Date: Tue, 14 Jul 2026 23:58:00 +0800 Subject: [PATCH] fix(antigravity): preserve manually entered refresh token --- .../components/account/CreateAccountModal.vue | 2 +- .../__tests__/useAntigravityOAuth.spec.ts | 56 +++++++++++++++++++ .../src/composables/useAntigravityOAuth.ts | 10 +++- 3 files changed, 65 insertions(+), 3 deletions(-) create mode 100644 frontend/src/composables/__tests__/useAntigravityOAuth.spec.ts diff --git a/frontend/src/components/account/CreateAccountModal.vue b/frontend/src/components/account/CreateAccountModal.vue index ea9deb7ba6..206c1f1494 100644 --- a/frontend/src/components/account/CreateAccountModal.vue +++ b/frontend/src/components/account/CreateAccountModal.vue @@ -5728,7 +5728,7 @@ const handleAntigravityValidateRT = async (refreshTokenInput: string) => { continue } - const credentials = antigravityOAuth.buildCredentials(tokenInfo) + const credentials = antigravityOAuth.buildCredentials(tokenInfo, refreshTokens[i]) applyAntigravityProjectID(credentials, antigravityProjectId.value, 'create') // Generate account name with index for batch diff --git a/frontend/src/composables/__tests__/useAntigravityOAuth.spec.ts b/frontend/src/composables/__tests__/useAntigravityOAuth.spec.ts new file mode 100644 index 0000000000..fcb6f48b18 --- /dev/null +++ b/frontend/src/composables/__tests__/useAntigravityOAuth.spec.ts @@ -0,0 +1,56 @@ +import { describe, expect, it, vi } from 'vitest' + +vi.mock('@/stores/app', () => ({ + useAppStore: () => ({ + showError: vi.fn() + }) +})) + +vi.mock('vue-i18n', () => ({ + useI18n: () => ({ + t: (key: string) => key + }) +})) + +vi.mock('@/api/admin', () => ({ + adminAPI: { + antigravity: { + generateAuthUrl: vi.fn(), + exchangeCode: vi.fn(), + refreshAntigravityToken: vi.fn() + } + } +})) + +import { useAntigravityOAuth } from '@/composables/useAntigravityOAuth' + +describe('useAntigravityOAuth.buildCredentials', () => { + it('falls back to the submitted refresh token when the response omits it', () => { + const oauth = useAntigravityOAuth() + + const credentials = oauth.buildCredentials( + { + access_token: 'access-token', + expires_at: 1_900_000_000 + }, + 'submitted-refresh-token' + ) + + expect(credentials.refresh_token).toBe('submitted-refresh-token') + }) + + it('prefers a new refresh token returned by the response', () => { + const oauth = useAntigravityOAuth() + + const credentials = oauth.buildCredentials( + { + access_token: 'access-token', + refresh_token: 'rotated-refresh-token', + expires_at: 1_900_000_000 + }, + 'submitted-refresh-token' + ) + + expect(credentials.refresh_token).toBe('rotated-refresh-token') + }) +}) diff --git a/frontend/src/composables/useAntigravityOAuth.ts b/frontend/src/composables/useAntigravityOAuth.ts index cf60fd097a..f480ea20bc 100644 --- a/frontend/src/composables/useAntigravityOAuth.ts +++ b/frontend/src/composables/useAntigravityOAuth.ts @@ -112,17 +112,23 @@ export function useAntigravityOAuth() { } } - const buildCredentials = (tokenInfo: AntigravityTokenInfo): Record => { + const buildCredentials = ( + tokenInfo: AntigravityTokenInfo, + fallbackRefreshToken?: string + ): Record => { let expiresAt: string | undefined if (typeof tokenInfo.expires_at === 'number' && Number.isFinite(tokenInfo.expires_at)) { expiresAt = Math.floor(tokenInfo.expires_at).toString() } else if (typeof tokenInfo.expires_at === 'string' && tokenInfo.expires_at.trim()) { expiresAt = tokenInfo.expires_at.trim() } + const refreshToken = tokenInfo.refresh_token?.trim() + ? tokenInfo.refresh_token + : fallbackRefreshToken return { access_token: tokenInfo.access_token, - refresh_token: tokenInfo.refresh_token, + refresh_token: refreshToken, token_type: tokenInfo.token_type, expires_at: expiresAt, project_id: tokenInfo.project_id,