* feat: implement JWT-based authentication system - Add bcrypt password hashing and JWT token generation - Create blocking auth modals for login and setup - Add authentication management to General Settings - Implement API routes for login, verify, setup, and credential management - Add AuthProvider and AuthGuard components - Support first-time setup and persistent authentication - Store credentials securely in .env file * feat: add option to skip enabling auth during setup - Add toggle in SetupModal to choose whether to enable authentication immediately - Users can set up credentials but keep authentication disabled initially - Authentication can be enabled/disabled later through General Settings - Maintains flexibility for users who want to configure auth gradually * fix: allow proceeding without password when auth is disabled - Make password fields optional when authentication is disabled in setup - Update button validation to only require password when auth is enabled - Modify API to handle optional password parameter - Update hasCredentials logic to work with username-only setup - Users can now complete setup with just username when auth is disabled - Password can be added later when enabling authentication * feat: don't store credentials when authentication is disabled - When auth is disabled, no username or password is stored - Setup modal only requires credentials when authentication is enabled - Disabling authentication clears all stored credentials - Users can skip authentication entirely without storing any data - Clean separation between enabled/disabled authentication states * feat: add setup completed flag to prevent modal on every load - Add AUTH_SETUP_COMPLETED flag to track when user has completed setup - Setup modal only appears when setupCompleted is false - Both enabled and disabled auth setups mark setup as completed - Clean .env file when authentication is disabled (no empty credential lines) - Prevents setup modal from appearing on every page load after user decision * fix: add missing Authentication tab button in settings modal - Authentication tab button was missing from the tabs navigation - Users couldn't access authentication settings - Added Authentication tab button with proper styling and click handler - Authentication settings are now accessible through the settings modal * fix: properly load and display authentication settings - Add setupCompleted state variable to track setup status - Update loadAuthCredentials to include setupCompleted field - Fix authentication status display logic to show correct state - Show proper status when auth is disabled but setup is completed - Enable toggle only when setup is completed (not just when credentials exist) - Settings now correctly reflect the actual authentication state * fix: handle empty FILTERS environment variable - Add check for empty or invalid FILTERS JSON before parsing - Prevents 'Unexpected end of JSON input' error when FILTERS is empty - Return null filters instead of throwing parse error - Clean up empty FILTERS line from .env file - Fixes console error when loading settings modal * fix: load authentication credentials when settings modal opens - Add loadAuthCredentials() call to useEffect when modal opens - Authentication settings were not loading because the function wasn't being called - Now properly loads auth configuration when settings modal is opened - Settings will display the correct authentication status and state * fix: prevent multiple JWT secret generation with caching - Add JWT secret caching to prevent race conditions - Multiple API calls were generating duplicate JWT secrets - Now caches secret after first generation/read - Clean up duplicate JWT_SECRET lines from .env file - Prevents .env file from being cluttered with multiple secrets * feat: auto-login user after setup with authentication enabled - When user sets up authentication with credentials, automatically log them in - Prevents need to manually log in after setup completion - Setup modal now calls login API after successful setup when auth is enabled - AuthGuard no longer reloads page after setup, just refreshes config - Seamless user experience from setup to authenticated state * fix: resolve console errors and improve auth flow - Fix 401 Unauthorized error by checking setup status before auth verification - AuthProvider now checks if setup is completed before attempting to verify auth - Prevents unnecessary auth verification calls when no credentials exist - Add webpack polling configuration to fix WebSocket HMR issues - Improves development experience when accessing from different IPs - Eliminates console errors during initial setup flow * fix: resolve build errors and linting issues - Fix TypeScript ESLint error: use optional chain expression in auth.ts - Fix React Hook warning: add missing 'isRunning' dependency to useEffect in Terminal.tsx - Build now compiles successfully without any errors or warnings - All linting rules are now satisfied
120 lines
3.2 KiB
TypeScript
120 lines
3.2 KiB
TypeScript
'use client';
|
|
|
|
import { createContext, useContext, useEffect, useState, type ReactNode } from 'react';
|
|
|
|
interface AuthContextType {
|
|
isAuthenticated: boolean;
|
|
username: string | null;
|
|
isLoading: boolean;
|
|
login: (username: string, password: string) => Promise<boolean>;
|
|
logout: () => void;
|
|
checkAuth: () => Promise<void>;
|
|
}
|
|
|
|
const AuthContext = createContext<AuthContextType | undefined>(undefined);
|
|
|
|
interface AuthProviderProps {
|
|
children: ReactNode;
|
|
}
|
|
|
|
export function AuthProvider({ children }: AuthProviderProps) {
|
|
const [isAuthenticated, setIsAuthenticated] = useState(false);
|
|
const [username, setUsername] = useState<string | null>(null);
|
|
const [isLoading, setIsLoading] = useState(true);
|
|
|
|
const checkAuth = async () => {
|
|
try {
|
|
// First check if setup is completed
|
|
const setupResponse = await fetch('/api/settings/auth-credentials');
|
|
if (setupResponse.ok) {
|
|
const setupData = await setupResponse.json() as { setupCompleted: boolean; enabled: boolean };
|
|
|
|
// If setup is not completed or auth is disabled, don't verify
|
|
if (!setupData.setupCompleted || !setupData.enabled) {
|
|
setIsAuthenticated(false);
|
|
setUsername(null);
|
|
setIsLoading(false);
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Only verify authentication if setup is completed and auth is enabled
|
|
const response = await fetch('/api/auth/verify');
|
|
if (response.ok) {
|
|
const data = await response.json() as { username: string };
|
|
setIsAuthenticated(true);
|
|
setUsername(data.username);
|
|
} else {
|
|
setIsAuthenticated(false);
|
|
setUsername(null);
|
|
}
|
|
} catch (error) {
|
|
console.error('Error checking auth:', error);
|
|
setIsAuthenticated(false);
|
|
setUsername(null);
|
|
} finally {
|
|
setIsLoading(false);
|
|
}
|
|
};
|
|
|
|
const login = async (username: string, password: string): Promise<boolean> => {
|
|
try {
|
|
const response = await fetch('/api/auth/login', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
},
|
|
body: JSON.stringify({ username, password }),
|
|
});
|
|
|
|
if (response.ok) {
|
|
const data = await response.json() as { username: string };
|
|
setIsAuthenticated(true);
|
|
setUsername(data.username);
|
|
return true;
|
|
} else {
|
|
const errorData = await response.json();
|
|
console.error('Login failed:', errorData.error);
|
|
return false;
|
|
}
|
|
} catch (error) {
|
|
console.error('Login error:', error);
|
|
return false;
|
|
}
|
|
};
|
|
|
|
const logout = () => {
|
|
// Clear the auth cookie by setting it to expire
|
|
document.cookie = 'auth-token=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;';
|
|
setIsAuthenticated(false);
|
|
setUsername(null);
|
|
};
|
|
|
|
useEffect(() => {
|
|
void checkAuth();
|
|
}, []);
|
|
|
|
return (
|
|
<AuthContext.Provider
|
|
value={{
|
|
isAuthenticated,
|
|
username,
|
|
isLoading,
|
|
login,
|
|
logout,
|
|
checkAuth,
|
|
}}
|
|
>
|
|
{children}
|
|
</AuthContext.Provider>
|
|
);
|
|
}
|
|
|
|
export function useAuth() {
|
|
const context = useContext(AuthContext);
|
|
if (context === undefined) {
|
|
throw new Error('useAuth must be used within an AuthProvider');
|
|
}
|
|
return context;
|
|
}
|