diff --git a/src/core/auth/auth.test.ts b/src/core/auth/auth.test.ts index 0c2925b8..93ddb1dc 100644 --- a/src/core/auth/auth.test.ts +++ b/src/core/auth/auth.test.ts @@ -100,6 +100,30 @@ describe('admin auth session', () => { expect(auth.refreshToken).toBe('refresh-domestic') }) + it('accepts serialized long lifetimes from the real authentication response', async () => { + mocks.post.mockResolvedValue({ + code: '200', + message: 'success', + data: { + tokenType: 'Bearer', + accessToken: token(), + expiresIn: '7200', + refreshToken: 'refresh-serialized', + refreshExpiresIn: '604800', + overseasAccessToken: null, + overseasExpiresIn: '0', + }, + }) + mocks.listCurrentPlatformPermissionCodes.mockResolvedValue(['platform:warehouse:list']) + const auth = useAuthStore() + + await auth.login('platform_admin', 'password') + + expect(auth.isAuthenticated).toBe(true) + expect(auth.refreshToken).toBe('refresh-serialized') + expect([...auth.permissionCodes]).toEqual(['platform:warehouse:list']) + }) + it('loads permissions from the current authorization endpoint when the JWT only contains identity', async () => { mocks.post.mockResolvedValue(loginResponse()) mocks.listCurrentPlatformPermissionCodes.mockResolvedValue(['platform:warehouse:list']) diff --git a/src/core/auth/auth.ts b/src/core/auth/auth.ts index 43ac8374..3a893a4f 100644 --- a/src/core/auth/auth.ts +++ b/src/core/auth/auth.ts @@ -21,16 +21,26 @@ const REFRESH_LOCK_NAME = 'cc-admin-auth-refresh' interface LoginResponse { tokenType: string accessToken: string - expiresIn: number + expiresIn: number | string refreshToken: string - refreshExpiresIn: number + refreshExpiresIn: number | string overseasAccessToken?: string | null - overseasExpiresIn?: number + overseasExpiresIn?: number | string } class ExpiredAccessTokenError extends Error {} class SupersededSessionError extends Error {} +function parseLifetimeSeconds(value: number | string, field: string) { + const normalized = typeof value === 'number' + ? value + : /^\d+$/.test(value.trim()) ? Number(value) : Number.NaN + if (!Number.isSafeInteger(normalized) || normalized < 0) { + throw new Error(`登录响应中的 ${field} 格式无效`) + } + return normalized +} + function sessionFromToken(token: string) { const claims = decodeJwtClaims(token) if (claims.app_code !== 'PLATFORM') throw new Error('账号不属于当前应用端') @@ -92,7 +102,7 @@ export const useAuthStore = defineStore('admin-auth', () => { ensureCurrentGeneration(expectedGeneration) replaceOverseasAccessToken( result.overseasAccessToken ?? null, - result.overseasExpiresIn ?? 0, + parseLifetimeSeconds(result.overseasExpiresIn ?? 0, 'overseasExpiresIn'), ) token.value = result.accessToken refreshToken.value = result.refreshToken