This is an automated email from the ASF dual-hosted git repository.

lizhimins pushed a commit to branch rocketmq-studio
in repository https://gitbox.apache.org/repos/asf/rocketmq-dashboard.git


The following commit(s) were added to refs/heads/rocketmq-studio by this push:
     new 23715480 fix: preserve sessions for public authentication failures 
(#717)
23715480 is described below

commit 237154807955189a821fdb922a38ea6a240eff8f
Author: Rui <[email protected]>
AuthorDate: Mon Aug 3 11:12:30 2026 +0800

    fix: preserve sessions for public authentication failures (#717)
---
 web/src/api/client.test.ts | 29 +++++++++++++++++++++++++++++
 web/src/api/client.ts      | 13 ++++++++++++-
 2 files changed, 41 insertions(+), 1 deletion(-)

diff --git a/web/src/api/client.test.ts b/web/src/api/client.test.ts
index 4be95ecb..27c31326 100644
--- a/web/src/api/client.test.ts
+++ b/web/src/api/client.test.ts
@@ -119,4 +119,33 @@ describe('API client response contract', () => {
 
     await client.get('/clusters');
   });
+
+  it.each(['/auth/login', '/auth/status'])(
+    'does not clear the current session when public auth request %s returns 
401',
+    async (path) => {
+      localStorage.setItem('token', 'current-token');
+      localStorage.setItem('rocketmq-studio-user', 'admin');
+      localStorage.setItem('rocketmq-studio-user-admin', 'true');
+      mock.onAny(path).reply(401, { code: 401, message: 'Unauthorized', data: 
null });
+
+      await expect(client.get(path)).rejects.toMatchObject({ response: { 
status: 401 } });
+
+      expect(localStorage.getItem('token')).toBe('current-token');
+      expect(localStorage.getItem('rocketmq-studio-user')).toBe('admin');
+      expect(localStorage.getItem('rocketmq-studio-user-admin')).toBe('true');
+    },
+  );
+
+  it('clears the current session when a protected API request returns 401', 
async () => {
+    localStorage.setItem('token', 'expired-token');
+    localStorage.setItem('rocketmq-studio-user', 'admin');
+    localStorage.setItem('rocketmq-studio-user-admin', 'true');
+    mock.onGet('/clusters').reply(401, { code: 401, message: 'Unauthorized', 
data: null });
+
+    await expect(client.get('/clusters')).rejects.toMatchObject({ response: { 
status: 401 } });
+
+    expect(localStorage.getItem('token')).toBeNull();
+    expect(localStorage.getItem('rocketmq-studio-user')).toBeNull();
+    expect(localStorage.getItem('rocketmq-studio-user-admin')).toBeNull();
+  });
 });
diff --git a/web/src/api/client.ts b/web/src/api/client.ts
index 41a97f1a..5b002a0f 100644
--- a/web/src/api/client.ts
+++ b/web/src/api/client.ts
@@ -21,6 +21,7 @@ import { clearAuthSession, TOKEN_STORAGE_KEY } from 
'../stores/authStorage';
 import { API_BASE_URL } from '../config';
 
 const SUCCESS_BUSINESS_CODES = new Set([0, 200]);
+const PUBLIC_AUTH_PATHS = new Set(['/auth/login', '/auth/status']);
 
 interface BusinessResponse {
   code?: unknown;
@@ -44,6 +45,16 @@ function getBusinessError(data: unknown): string | null {
   return typeof data.message === 'string' && data.message.trim() ? 
data.message : '请求失败';
 }
 
+function isPublicAuthRequest(url?: string): boolean {
+  if (!url) return false;
+  const requestPath = new URL(url, window.location.origin).pathname;
+  const apiBasePath = new URL(API_BASE_URL, window.location.origin).pathname;
+  const relativePath = requestPath.startsWith(`${apiBasePath}/`)
+    ? requestPath.slice(apiBasePath.length)
+    : requestPath;
+  return PUBLIC_AUTH_PATHS.has(relativePath);
+}
+
 const client = axios.create({
   baseURL: API_BASE_URL,
   timeout: 30000,
@@ -72,7 +83,7 @@ client.interceptors.response.use(
     return response;
   },
   (error) => {
-    if (error.response?.status === 401) {
+    if (error.response?.status === 401 && 
!isPublicAuthRequest(error.config?.url)) {
       clearAuthSession();
       window.location.href = '/';
     }

Reply via email to