From 059e8bf56200c201b2256fa69f7eac4d0777762a Mon Sep 17 00:00:00 2001 From: rcourtman Date: Wed, 5 Nov 2025 19:36:01 +0000 Subject: [PATCH] Redirect to login when authentication expires Related to #626 When authentication expires after some time, users see "Connection lost" and must refresh the page to see "Authentication required". This commit implements automatic redirect to login when authentication expires. Changes: - Add authentication check to WebSocket endpoint to prevent unauthenticated WebSocket connections - Handle WebSocket close with code 1008 (policy violation) as auth failure and redirect to login - Intercept 401 responses on API calls (except initial auth checks) and automatically redirect to login page - Clear stored credentials and set logout flag before redirect to ensure clean login flow This provides a better user experience by immediately redirecting to the login page when the session expires, rather than showing a confusing "Connection lost" message that requires manual page refresh. --- frontend-modern/src/stores/websocket.ts | 12 ++++++++++++ frontend-modern/src/utils/apiClient.ts | 16 +++++++++++----- internal/api/router.go | 4 ++++ 3 files changed, 27 insertions(+), 5 deletions(-) diff --git a/frontend-modern/src/stores/websocket.ts b/frontend-modern/src/stores/websocket.ts index 722fbd584..076d3ad4a 100644 --- a/frontend-modern/src/stores/websocket.ts +++ b/frontend-modern/src/stores/websocket.ts @@ -611,6 +611,18 @@ export function createWebSocketStore(url: string) { return; } + // If we get a 1008 (policy violation) close code, it's likely an auth failure + // Redirect to login page to re-authenticate + if (event.code === 1008) { + logger.warn('WebSocket closed due to authentication failure, redirecting to login'); + // Clear auth and reload to trigger login + if (typeof window !== 'undefined') { + localStorage.setItem('just_logged_out', 'true'); + window.location.href = '/'; + } + return; + } + handleReconnect(); }; diff --git a/frontend-modern/src/utils/apiClient.ts b/frontend-modern/src/utils/apiClient.ts index b5d86104c..177d84148 100644 --- a/frontend-modern/src/utils/apiClient.ts +++ b/frontend-modern/src/utils/apiClient.ts @@ -219,11 +219,17 @@ class ApiClient { const response = await fetch(url, finalOptions); - // If we get a 401, our auth might be invalid - if (response.status === 401 && this.hasAuth()) { - // Could trigger a re-login flow here - logger.warn('Authentication failed - credentials may be incorrect'); - // Don't clear auth automatically - let the user retry + // If we get a 401 on an API call (not during initial auth check), redirect to login + // Skip redirect for specific auth-check endpoints to avoid loops + if (response.status === 401 && !url.includes('/api/security/status') && !url.includes('/api/state')) { + logger.warn('Authentication expired - redirecting to login'); + // Clear auth and redirect to login + if (typeof window !== 'undefined') { + this.clearAuth(); + localStorage.setItem('just_logged_out', 'true'); + window.location.href = '/'; + } + return response; } // Handle CSRF token failures diff --git a/internal/api/router.go b/internal/api/router.go index d18390292..42ac62767 100644 --- a/internal/api/router.go +++ b/internal/api/router.go @@ -2772,6 +2772,10 @@ func (r *Router) handleSnapshots(w http.ResponseWriter, req *http.Request) { // handleWebSocket handles WebSocket connections func (r *Router) handleWebSocket(w http.ResponseWriter, req *http.Request) { + // Check authentication before allowing WebSocket upgrade + if !CheckAuth(r.config, w, req) { + return + } r.wsHub.HandleWebSocket(w, req) }