diff --git a/vehicle-data-platform/apps/web/src/v2/auth/AuthGate.test.tsx b/vehicle-data-platform/apps/web/src/v2/auth/AuthGate.test.tsx
index 5218874f..ccdf763b 100644
--- a/vehicle-data-platform/apps/web/src/v2/auth/AuthGate.test.tsx
+++ b/vehicle-data-platform/apps/web/src/v2/auth/AuthGate.test.tsx
@@ -105,6 +105,19 @@ test('exposes only account and password login on the public sign-in screen', asy
expect(screen.getByRole('button', { name: '登录工作台' })).toBeDisabled();
});
+test('silently clears an expired stored session without exposing token errors on the account login page', async () => {
+ const client = new QueryClient({ defaultOptions: { queries: { retry: false } } });
+ setAccessToken('expired-session-token');
+ mocks.session.mockRejectedValue(new Error('需要有效的访问令牌'));
+
+ render( undefined} />);
+
+ expect(await screen.findByRole('form', { name: '账号密码登录' })).toBeInTheDocument();
+ expect(screen.queryByRole('alert')).not.toBeInTheDocument();
+ expect(screen.queryByText(/访问令牌/)).not.toBeInTheDocument();
+ await waitFor(() => expect(getAccessToken()).toBe(''));
+});
+
test('an expired protected request returns to login and removes the active user cache', async () => {
mocks.logout.mockResolvedValue({ loggedOut: true });
const client = new QueryClient({ defaultOptions: { queries: { retry: false } } });
diff --git a/vehicle-data-platform/apps/web/src/v2/auth/AuthGate.tsx b/vehicle-data-platform/apps/web/src/v2/auth/AuthGate.tsx
index c77dc465..41baaad0 100644
--- a/vehicle-data-platform/apps/web/src/v2/auth/AuthGate.tsx
+++ b/vehicle-data-platform/apps/web/src/v2/auth/AuthGate.tsx
@@ -107,6 +107,11 @@ export function AuthGate({ children }: { children: ReactNode }) {
window.addEventListener(PLATFORM_UNAUTHORIZED_EVENT, finishLogout);
return () => window.removeEventListener(PLATFORM_UNAUTHORIZED_EVENT, finishLogout);
}, [finishLogout]);
+ useEffect(() => {
+ if (!session.isError || loginPending || loginError || !getAccessToken()) return;
+ clearAccessToken();
+ setAttempted(false);
+ }, [loginError, loginPending, session.isError]);
const login = async (event: FormEvent) => {
event.preventDefault();
@@ -129,7 +134,7 @@ export function AuthGate({ children }: { children: ReactNode }) {
return ;
}
if (!session.data) {
- const error = loginError || (attempted && session.error ? session.error.message : '');
+ const error = loginError || (attempted && session.error && !getAccessToken() ? session.error.message : '');
return