From dd26de9f5518ee39bddb50247e563c2b24767325 Mon Sep 17 00:00:00 2001 From: DhanushSantosh Date: Sun, 18 Jan 2026 21:13:10 +0530 Subject: [PATCH] fix: add authentication validation to image endpoint for web mode Adds authentication checks to the /api/fs/image endpoint to validate session tokens in web mode. This ensures background images and other image assets load correctly in web mode by validating: - session token from query parameter (web mode) - API key from query parameter (Electron mode) - session cookie (web mode fallback) - X-API-Key and X-Session-Token headers This fixes the issue where kanban board background images were not visible in web mode because the image request lacked proper authentication. Co-Authored-By: Claude Haiku 4.5 --- apps/server/src/routes/fs/routes/image.ts | 55 +++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/apps/server/src/routes/fs/routes/image.ts b/apps/server/src/routes/fs/routes/image.ts index b7e8c214..97187528 100644 --- a/apps/server/src/routes/fs/routes/image.ts +++ b/apps/server/src/routes/fs/routes/image.ts @@ -1,16 +1,33 @@ /** * GET /image endpoint - Serve image files + * + * Requires authentication via: + * - apiKey query parameter (Electron mode) + * - token query parameter (web mode) + * - session cookie (web mode) + * - X-API-Key header (Electron mode) + * - X-Session-Token header (web mode) */ import type { Request, Response } from 'express'; import * as secureFs from '../../../lib/secure-fs.js'; import path from 'path'; import { PathNotAllowedError } from '@automaker/platform'; +import { validateApiKey, validateSession } from '../../../lib/auth.js'; import { getErrorMessage, logError } from '../common.js'; +const SESSION_COOKIE_NAME = 'automaker_session'; + export function createImageHandler() { return async (req: Request, res: Response): Promise => { try { + // Authenticate the request + const isAuthenticated = checkImageAuthentication(req); + if (!isAuthenticated) { + res.status(401).json({ success: false, error: 'Authentication required' }); + return; + } + const { path: imagePath, projectPath } = req.query as { path?: string; projectPath?: string; @@ -64,3 +81,41 @@ export function createImageHandler() { } }; } + +/** + * Check if image request is authenticated + * Supports multiple authentication methods + */ +function checkImageAuthentication(req: Request): boolean { + // Check for API key in header (Electron mode) + const headerKey = req.get('x-api-key'); + if (headerKey && validateApiKey(headerKey)) { + return true; + } + + // Check for session token in header (web mode) + const sessionTokenHeader = req.get('x-session-token'); + if (sessionTokenHeader && validateSession(sessionTokenHeader)) { + return true; + } + + // Check for API key in query parameter (fallback) + const queryKey = req.query.apiKey as string | undefined; + if (queryKey && validateApiKey(queryKey)) { + return true; + } + + // Check for session token in query parameter (web mode with token) + const queryToken = req.query.token as string | undefined; + if (queryToken && validateSession(queryToken)) { + return true; + } + + // Check for session cookie (web mode) + const sessionCookie = req.cookies?.[SESSION_COOKIE_NAME]; + if (sessionCookie && validateSession(sessionCookie)) { + return true; + } + + return false; +}