"use client"; import { useState, useEffect, useCallback } from 'react'; import { motion, AnimatePresence } from 'framer-motion'; import { Lock, Eye, EyeOff, Shield, AlertTriangle, CheckCircle, XCircle, Loader2 } from 'lucide-react'; import ModernAdminDashboard from '@/components/ModernAdminDashboard'; // Security constants const MAX_ATTEMPTS = 3; const LOCKOUT_DURATION = 15 * 60 * 1000; // 15 minutes const SESSION_DURATION = 2 * 60 * 60 * 1000; // 2 hours (reduced from 24h) const RATE_LIMIT_DELAY = 1000; // 1 second base delay // Password hashing removed - now handled server-side securely // Rate limiting with exponential backoff const getRateLimitDelay = (attempts: number): number => { return RATE_LIMIT_DELAY * Math.pow(2, attempts); }; interface AuthState { isAuthenticated: boolean; isLoading: boolean; showLogin: boolean; password: string; showPassword: boolean; error: string; attempts: number; isLocked: boolean; lastAttempt: number; csrfToken: string; } const AdminPage = () => { const [authState, setAuthState] = useState({ isAuthenticated: false, isLoading: true, showLogin: false, password: '', showPassword: false, error: '', attempts: 0, isLocked: false, lastAttempt: 0, csrfToken: '' }); // Fetch CSRF token const fetchCSRFToken = useCallback(async () => { try { const response = await fetch('/api/auth/csrf'); const data = await response.json(); if (response.ok && data.csrfToken) { setAuthState(prev => ({ ...prev, csrfToken: data.csrfToken })); return data.csrfToken; } } catch (error) { console.error('Failed to fetch CSRF token:', error); } return ''; }, []); // Check if user is locked out const checkLockout = useCallback(() => { const lockoutData = localStorage.getItem('admin_lockout'); if (lockoutData) { try { const { timestamp, attempts } = JSON.parse(lockoutData); const now = Date.now(); if (now - timestamp < LOCKOUT_DURATION) { setAuthState(prev => ({ ...prev, isLocked: true, attempts, isLoading: false })); return true; } else { localStorage.removeItem('admin_lockout'); } } catch (error) { localStorage.removeItem('admin_lockout'); } } return false; }, []); // Check session validity via API const checkSession = useCallback(async () => { const authStatus = sessionStorage.getItem('admin_authenticated'); const sessionToken = sessionStorage.getItem('admin_session_token'); if (!authStatus || !sessionToken) { setAuthState(prev => ({ ...prev, showLogin: true, isLoading: false })); return false; } try { // Validate session with server const response = await fetch('/api/auth/validate', { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-CSRF-Token': authState.csrfToken }, body: JSON.stringify({ sessionToken, csrfToken: authState.csrfToken }) }); const data = await response.json(); if (response.ok && data.valid) { setAuthState(prev => ({ ...prev, isAuthenticated: true, isLoading: false, showLogin: false })); return true; } else { // Session invalid, clear storage sessionStorage.clear(); setAuthState(prev => ({ ...prev, showLogin: true, isLoading: false })); return false; } } catch (error) { // Network error, clear session sessionStorage.clear(); setAuthState(prev => ({ ...prev, showLogin: true, isLoading: false })); return false; } }, []); // Initialize authentication check useEffect(() => { const initAuth = async () => { // Add random delay to prevent timing attacks await new Promise(resolve => setTimeout(resolve, Math.random() * 500 + 200)); // Fetch CSRF token first await fetchCSRFToken(); if (!checkLockout()) { await checkSession(); } }; initAuth(); }, [checkLockout, checkSession, fetchCSRFToken]); // Handle login submission const handleLogin = async (e: React.FormEvent) => { e.preventDefault(); if (authState.isLocked || authState.isLoading) return; setAuthState(prev => ({ ...prev, isLoading: true, error: '' })); try { // Rate limiting delay const delay = getRateLimitDelay(authState.attempts); await new Promise(resolve => setTimeout(resolve, delay)); // Send login request to secure API const response = await fetch('/api/auth/login', { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-CSRF-Token': authState.csrfToken }, body: JSON.stringify({ password: authState.password, csrfToken: authState.csrfToken }) }); const data = await response.json(); if (response.ok && data.success) { // Successful login const now = Date.now(); const sessionToken = data.sessionToken; localStorage.removeItem('admin_lockout'); sessionStorage.setItem('admin_authenticated', 'true'); sessionStorage.setItem('admin_login_time', now.toString()); sessionStorage.setItem('admin_session_token', sessionToken); setAuthState(prev => ({ ...prev, isAuthenticated: true, showLogin: false, isLoading: false, password: '', attempts: 0, error: '' })); } else { // Failed login const newAttempts = authState.attempts + 1; const newLastAttempt = Date.now(); if (newAttempts >= MAX_ATTEMPTS) { // Lock user out localStorage.setItem('admin_lockout', JSON.stringify({ timestamp: newLastAttempt, attempts: newAttempts })); setAuthState(prev => ({ ...prev, isLocked: true, attempts: newAttempts, lastAttempt: newLastAttempt, isLoading: false, error: `Zu viele fehlgeschlagene Versuche. Zugang für ${Math.ceil(LOCKOUT_DURATION / 60000)} Minuten gesperrt.` })); } else { setAuthState(prev => ({ ...prev, attempts: newAttempts, lastAttempt: newLastAttempt, isLoading: false, error: data.error || `Falsches Passwort. ${MAX_ATTEMPTS - newAttempts} Versuche übrig.`, password: '' })); } } } catch (error) { setAuthState(prev => ({ ...prev, isLoading: false, error: 'Ein Fehler ist aufgetreten. Bitte versuchen Sie es erneut.' })); } }; // Handle logout const handleLogout = () => { sessionStorage.clear(); setAuthState(prev => ({ ...prev, isAuthenticated: false, showLogin: true, password: '', error: '' })); }; // Get remaining lockout time const getRemainingTime = () => { const lockoutData = localStorage.getItem('admin_lockout'); if (lockoutData) { try { const { timestamp } = JSON.parse(lockoutData); const remaining = Math.ceil((LOCKOUT_DURATION - (Date.now() - timestamp)) / 1000 / 60); return Math.max(0, remaining); } catch { return 0; } } return 0; }; // Loading state if (authState.isLoading && !authState.showLogin) { return (

Überprüfe Berechtigung...

); } // Lockout state if (authState.isLocked) { return (

Zugang gesperrt

Zu viele fehlgeschlagene Anmeldeversuche

Versuche: {authState.attempts}/{MAX_ATTEMPTS}

Verbleibende Zeit: {getRemainingTime()} Minuten

Der Zugang wird automatisch nach {Math.ceil(LOCKOUT_DURATION / 60000)} Minuten freigeschaltet.

); } // Login form if (authState.showLogin || !authState.isAuthenticated) { return (

Admin-Zugang

Bitte geben Sie das Admin-Passwort ein

setAuthState(prev => ({ ...prev, password: e.target.value }))} className="w-full px-4 py-3 bg-white/10 border border-white/20 rounded-lg text-white placeholder-gray-400 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent transition-all pr-12" placeholder="Admin-Passwort eingeben" required disabled={authState.isLoading} autoComplete="current-password" />
{authState.error && (

{authState.error}

)}

Versuche: {authState.attempts}/{MAX_ATTEMPTS}

); } // Authenticated state - show admin dashboard return (
{/* Logout button */}
); }; export default AdminPage;