From 5a09547532754da21faacb5351cc3538a9db05a4 Mon Sep 17 00:00:00 2001 From: Tam Nhu Tran Date: Sun, 29 Mar 2026 10:54:44 -0400 Subject: [PATCH] fix(auth): signal auth-required for remote access in /api/auth/check The auth check endpoint always returned authRequired=false when auth was disabled, even for remote clients. This caused the UI to render the dashboard directly, where all data API calls return 403 silently. Now detects remote access via isLoopbackRemoteAddress and sets effectiveAuthRequired=true, so the UI properly redirects to the login page. Also fixes misleading catch handler comment in auth-context. --- src/web-server/routes/auth-routes.ts | 10 ++++++++-- ui/src/contexts/auth-context.tsx | 5 +++-- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/web-server/routes/auth-routes.ts b/src/web-server/routes/auth-routes.ts index 3a7efb3b..70b2564f 100644 --- a/src/web-server/routes/auth-routes.ts +++ b/src/web-server/routes/auth-routes.ts @@ -7,7 +7,7 @@ import { Router, type Request, type Response } from 'express'; import bcrypt from 'bcrypt'; import crypto from 'crypto'; import { getDashboardAuthConfig } from '../../config/unified-config-loader'; -import { loginRateLimiter } from '../middleware/auth-middleware'; +import { isLoopbackRemoteAddress, loginRateLimiter } from '../middleware/auth-middleware'; /** * Timing-safe string comparison to prevent timing attacks. @@ -94,9 +94,15 @@ router.post('/logout', (req: Request, res: Response) => { */ router.get('/check', (req: Request, res: Response) => { const authConfig = getDashboardAuthConfig(); + const isLocal = isLoopbackRemoteAddress(req.socket.remoteAddress); + + // When auth is not configured and access is remote, the dashboard API + // endpoints return 403. Signal auth-required so the UI can show the + // login/setup page instead of a silently broken dashboard. + const effectiveAuthRequired = authConfig.enabled || !isLocal; res.json({ - authRequired: authConfig.enabled, + authRequired: effectiveAuthRequired, authenticated: req.session?.authenticated ?? false, username: req.session?.username ?? null, }); diff --git a/ui/src/contexts/auth-context.tsx b/ui/src/contexts/auth-context.tsx index f43993a2..d0e39ee7 100644 --- a/ui/src/contexts/auth-context.tsx +++ b/ui/src/contexts/auth-context.tsx @@ -47,8 +47,9 @@ export function AuthProvider({ children }: { children: ReactNode }) { setUsername(res.username); }) .catch(() => { - // If auth check fails (e.g., 403 from remote access without auth configured), - // treat as auth required so the login page appears instead of a broken dashboard. + // If auth check fails (network error, server down, CORS issue), + // fail closed: require auth instead of granting access. + // Prevents silently broken dashboard when server is unreachable. setAuthRequired(true); setIsAuthenticated(false); })