= $maxAttempts && ($currentTime - $attemptData['last_attempt']) < $lockoutTime) { echo json_encode(["error" => "Too many failed login attempts. Please try again later."]); exit; } } // --- Authentication Function --- function authenticate($username, $password) { global $usersFile; if (!file_exists($usersFile)) { return false; } $lines = file($usersFile, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES); foreach ($lines as $line) { list($storedUser, $storedPass, $storedRole) = explode(':', trim($line), 3); if ($username === $storedUser && password_verify($password, $storedPass)) { return $storedRole; // Return the user's role } } return false; } // Get JSON input. $data = json_decode(file_get_contents("php://input"), true); $username = trim($data["username"] ?? ""); $password = trim($data["password"] ?? ""); $rememberMe = isset($data["remember_me"]) && $data["remember_me"] === true; // Validate input: ensure both fields are provided. if (!$username || !$password) { echo json_encode(["error" => "Username and password are required"]); exit; } // Validate username format. if (!preg_match('/^[A-Za-z0-9_\- ]+$/', $username)) { echo json_encode(["error" => "Invalid username format. Only letters, numbers, underscores, dashes, and spaces are allowed."]); exit; } // Attempt to authenticate the user. $userRole = authenticate($username, $password); if ($userRole !== false) { // On successful login, reset failed attempts for this IP. if (isset($failedAttempts[$ip])) { unset($failedAttempts[$ip]); saveFailedAttempts($attemptsFile, $failedAttempts); } // Regenerate session ID to mitigate session fixation attacks. session_regenerate_id(true); $_SESSION["authenticated"] = true; $_SESSION["username"] = $username; $_SESSION["isAdmin"] = ($userRole === "1"); // "1" indicates admin // If "Remember me" is checked, generate a persistent login token. if ($rememberMe) { // Generate a secure random token. $token = bin2hex(random_bytes(32)); $expiry = time() + (30 * 24 * 60 * 60); // 30 days // Load existing persistent tokens. $persistentTokens = []; if (file_exists($persistentTokensFile)) { $encryptedContent = file_get_contents($persistentTokensFile); $decryptedContent = decryptData($encryptedContent, $encryptionKey); $persistentTokens = json_decode($decryptedContent, true); if (!is_array($persistentTokens)) { $persistentTokens = []; } } // Save token along with username, expiry, and admin status. $persistentTokens[$token] = [ "username" => $username, "expiry" => $expiry, "isAdmin" => ($userRole === "1") ]; $encryptedContent = encryptData(json_encode($persistentTokens, JSON_PRETTY_PRINT), $encryptionKey); file_put_contents($persistentTokensFile, $encryptedContent, LOCK_EX); // Set the cookie. (Assuming $secure is defined in config.php.) setcookie('remember_me_token', $token, $expiry, '/', '', $secure, true); } echo json_encode(["success" => "Login successful", "isAdmin" => $_SESSION["isAdmin"]]); } else { // On failed login, update failed attempts. if (isset($failedAttempts[$ip])) { $failedAttempts[$ip]['count']++; $failedAttempts[$ip]['last_attempt'] = $currentTime; } else { $failedAttempts[$ip] = ['count' => 1, 'last_attempt' => $currentTime]; } saveFailedAttempts($attemptsFile, $failedAttempts); // Log the failed attempt to the plain text log for fail2ban. $logLine = date('Y-m-d H:i:s') . " - Failed login attempt for username: " . $username . " from IP: " . $ip . PHP_EOL; file_put_contents($failedLogFile, $logLine, FILE_APPEND); echo json_encode(["error" => "Invalid credentials"]); } ?>