From 8546d8bbd84edac2cf55883e2c2a28db26196c81 Mon Sep 17 00:00:00 2001 From: Riajul Islam Date: Sun, 24 Aug 2025 05:23:02 +0000 Subject: [PATCH] feat: add AUTH_DISABLED environment variable to disable authentication MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This feature allows users to bypass authentication entirely by setting AUTH_DISABLED=true in their environment variables. Changes: - Added AUTH_DISABLED environment variable support - Updated .env.example with AUTH_DISABLED=false default - Modified authentication middleware to skip auth when disabled - Updated WebSocket authentication to handle disabled mode - Updated frontend components to handle auth disabled state - Added proper status responses for auth disabled mode When AUTH_DISABLED=true: - All API endpoints become accessible without authentication - WebSocket connections work without tokens - Frontend skips login/setup forms and provides direct access - Mock user object (id: 1, username: 'admin') used for compatibility 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- .env.example | 11 ++++++++++- server/index.js | 13 ++++++++++++- server/middleware/auth.js | 25 +++++++++++++++++++++++++ server/routes/auth.js | 30 +++++++++++++++++++++++++++--- src/components/ProtectedRoute.jsx | 7 ++++++- src/contexts/AuthContext.jsx | 14 ++++++++++++++ src/utils/api.js | 2 +- src/utils/websocket.js | 22 ++++++++++++++++++++-- 8 files changed, 115 insertions(+), 9 deletions(-) diff --git a/.env.example b/.env.example index 7cab2722..e6f09c92 100755 --- a/.env.example +++ b/.env.example @@ -9,4 +9,13 @@ #API server PORT=3001 #Frontend port -VITE_PORT=5173 \ No newline at end of file +VITE_PORT=5173 + +# ============================================================================= +# AUTHENTICATION CONFIGURATION +# ============================================================================= + +# Disable authentication entirely (default: false) +# When set to true, all features become accessible without login +# WARNING: Only use in secure, private environments +AUTH_DISABLED=false \ No newline at end of file diff --git a/server/index.js b/server/index.js index ca451337..97dcc2d5 100755 --- a/server/index.js +++ b/server/index.js @@ -44,7 +44,7 @@ import authRoutes from './routes/auth.js'; import mcpRoutes from './routes/mcp.js'; import cursorRoutes from './routes/cursor.js'; import { initializeDatabase } from './database/db.js'; -import { validateApiKey, authenticateToken, authenticateWebSocket } from './middleware/auth.js'; +import { validateApiKey, authenticateToken, authenticateWebSocket, isAuthDisabled } from './middleware/auth.js'; // File system watcher for projects folder let projectsWatcher = null; @@ -143,6 +143,17 @@ const wss = new WebSocketServer({ verifyClient: (info) => { console.log('WebSocket connection attempt to:', info.req.url); + // Skip authentication if AUTH_DISABLED is true + if (isAuthDisabled()) { + // Store mock user info for compatibility + info.req.user = { + userId: 1, + username: 'admin' + }; + console.log('✅ WebSocket authentication bypassed (AUTH_DISABLED=true)'); + return true; + } + // Extract token from query parameters or headers const url = new URL(info.req.url, 'http://localhost'); const token = url.searchParams.get('token') || diff --git a/server/middleware/auth.js b/server/middleware/auth.js index 433c3293..001a4611 100644 --- a/server/middleware/auth.js +++ b/server/middleware/auth.js @@ -4,6 +4,11 @@ import { userDb } from '../database/db.js'; // Get JWT secret from environment or use default (for development) const JWT_SECRET = process.env.JWT_SECRET || 'claude-ui-dev-secret-change-in-production'; +// Check if authentication is disabled +const isAuthDisabled = () => { + return process.env.AUTH_DISABLED === 'true'; +}; + // Optional API key middleware const validateApiKey = (req, res, next) => { // Skip API key validation if not configured @@ -20,6 +25,16 @@ const validateApiKey = (req, res, next) => { // JWT authentication middleware const authenticateToken = async (req, res, next) => { + // Skip authentication if AUTH_DISABLED is true + if (isAuthDisabled()) { + // Create a mock user object for compatibility + req.user = { + id: 1, + username: 'admin' + }; + return next(); + } + const authHeader = req.headers['authorization']; const token = authHeader && authHeader.split(' ')[1]; // Bearer TOKEN @@ -58,6 +73,15 @@ const generateToken = (user) => { // WebSocket authentication function const authenticateWebSocket = (token) => { + // Skip authentication if AUTH_DISABLED is true + if (isAuthDisabled()) { + // Return a mock user object for compatibility + return { + userId: 1, + username: 'admin' + }; + } + if (!token) { return null; } @@ -76,5 +100,6 @@ export { authenticateToken, generateToken, authenticateWebSocket, + isAuthDisabled, JWT_SECRET }; \ No newline at end of file diff --git a/server/routes/auth.js b/server/routes/auth.js index 82a7c0d8..9edf6c06 100644 --- a/server/routes/auth.js +++ b/server/routes/auth.js @@ -1,17 +1,28 @@ import express from 'express'; import bcrypt from 'bcrypt'; import { userDb, db } from '../database/db.js'; -import { generateToken, authenticateToken } from '../middleware/auth.js'; +import { generateToken, authenticateToken, isAuthDisabled } from '../middleware/auth.js'; const router = express.Router(); // Check auth status and setup requirements router.get('/status', async (req, res) => { try { + // If authentication is disabled, return authenticated status + if (isAuthDisabled()) { + res.json({ + needsSetup: false, + isAuthenticated: true, + authDisabled: true + }); + return; + } + const hasUsers = await userDb.hasUsers(); res.json({ needsSetup: !hasUsers, - isAuthenticated: false // Will be overridden by frontend if token exists + isAuthenticated: false, // Will be overridden by frontend if token exists + authDisabled: false }); } catch (error) { console.error('Auth status error:', error); @@ -120,8 +131,21 @@ router.post('/login', async (req, res) => { // Get current user (protected route) router.get('/user', authenticateToken, (req, res) => { + // If authentication is disabled, return mock user + if (isAuthDisabled()) { + res.json({ + user: { + id: 1, + username: 'admin' + }, + authDisabled: true + }); + return; + } + res.json({ - user: req.user + user: req.user, + authDisabled: false }); }); diff --git a/src/components/ProtectedRoute.jsx b/src/components/ProtectedRoute.jsx index 88b404bb..c1e9d56c 100644 --- a/src/components/ProtectedRoute.jsx +++ b/src/components/ProtectedRoute.jsx @@ -24,12 +24,17 @@ const LoadingScreen = () => ( ); const ProtectedRoute = ({ children }) => { - const { user, isLoading, needsSetup } = useAuth(); + const { user, isLoading, needsSetup, authDisabled } = useAuth(); if (isLoading) { return ; } + // If authentication is disabled, allow access immediately + if (authDisabled) { + return children; + } + if (needsSetup) { return ; } diff --git a/src/contexts/AuthContext.jsx b/src/contexts/AuthContext.jsx index 77acb6c6..98df7b40 100644 --- a/src/contexts/AuthContext.jsx +++ b/src/contexts/AuthContext.jsx @@ -9,6 +9,7 @@ const AuthContext = createContext({ logout: () => {}, isLoading: true, needsSetup: false, + authDisabled: false, error: null }); @@ -25,6 +26,7 @@ export const AuthProvider = ({ children }) => { const [token, setToken] = useState(localStorage.getItem('auth-token')); const [isLoading, setIsLoading] = useState(true); const [needsSetup, setNeedsSetup] = useState(false); + const [authDisabled, setAuthDisabled] = useState(false); const [error, setError] = useState(null); // Check authentication status on mount @@ -41,6 +43,17 @@ export const AuthProvider = ({ children }) => { const statusResponse = await api.auth.status(); const statusData = await statusResponse.json(); + // Handle authentication disabled mode + if (statusData.authDisabled) { + setAuthDisabled(true); + setUser({ id: 1, username: 'admin' }); + setNeedsSetup(false); + // Create a dummy token for frontend compatibility + setToken('auth-disabled-token'); + setIsLoading(false); + return; + } + if (statusData.needsSetup) { setNeedsSetup(true); setIsLoading(false); @@ -147,6 +160,7 @@ export const AuthProvider = ({ children }) => { logout, isLoading, needsSetup, + authDisabled, error }; diff --git a/src/utils/api.js b/src/utils/api.js index 22978512..9f1ebf7c 100644 --- a/src/utils/api.js +++ b/src/utils/api.js @@ -1,6 +1,6 @@ // Utility function for authenticated API calls export const authenticatedFetch = (url, options = {}) => { - const token = localStorage.getItem('auth-token'); + const token = localStorage.getItem('auth-token') || 'auth-disabled-token'; const defaultHeaders = { 'Content-Type': 'application/json', diff --git a/src/utils/websocket.js b/src/utils/websocket.js index f03fd002..9efc3fa7 100755 --- a/src/utils/websocket.js +++ b/src/utils/websocket.js @@ -21,13 +21,31 @@ export function useWebSocket() { const connect = async () => { try { + // Check if authentication is disabled by checking auth status + let authDisabled = false; + let token = localStorage.getItem('auth-token'); + + try { + const statusResponse = await fetch('/api/auth/status'); + if (statusResponse.ok) { + const statusData = await statusResponse.json(); + authDisabled = statusData.authDisabled; + } + } catch (error) { + console.warn('Could not check auth status:', error); + } + // Get authentication token - const token = localStorage.getItem('auth-token'); - if (!token) { + if (!authDisabled && !token) { console.warn('No authentication token found for WebSocket connection'); return; } + // Use dummy token if authentication is disabled + if (authDisabled && !token) { + token = 'auth-disabled-token'; + } + // Fetch server configuration to get the correct WebSocket URL let wsBaseUrl; try {