'error', 'message' => 'Database connection failed.']); exit(); } // Google Client Setup $client = new Google_Client(); $client->setClientId($_ENV['GOOGLE_CLIENT_ID']); $client->setClientSecret($_ENV['GOOGLE_CLIENT_SECRET']); $client->setRedirectUri($_ENV['HOST'] . '/validate_login.php'); $client->addScope("email"); $client->addScope("profile"); // 👇 Add this to force the account picker $client->setPrompt('select_account'); // Check if Google login code is set if (isset($_GET['code'])) { $token = $client->fetchAccessTokenWithAuthCode($_GET['code']); if (!isset($token["error"])) { $client->setAccessToken($token['access_token']); $google_oauth = new Google_Service_Oauth2($client); $google_account_info = $google_oauth->userinfo->get(); // Get user info from Google $email = $google_account_info->email; $name = $google_account_info->name; $first_name = $google_account_info->given_name; $last_name = $google_account_info->family_name; $picture = $google_account_info->picture; // Check if the user exists in the database $query = "SELECT * FROM users WHERE email = ?"; $stmt = $conn->prepare($query); $stmt->bind_param("s", $email); $stmt->execute(); $result = $stmt->get_result(); if ($result->num_rows == 0) { // User does not exist, so register them $password = null; // No password for Google login $query = "INSERT INTO users (email, first_name, last_name, profile_pic, password, is_verified) VALUES (?, ?, ?, ?, ?, ?)"; $stmt = $conn->prepare($query); $is_verified = 1; // Assuming Google users are considered verified $stmt->bind_param("sssssi", $email, $first_name, $last_name, $picture, $password, $is_verified); if ($stmt->execute()) { // User successfully registered, set session and redirect $_SESSION['user_id'] = $conn->insert_id; $_SESSION['first_name'] = $first_name; $_SESSION['profile_pic'] = $picture; // Send Notification $event = 'user_login'; $sub_feed = 'logins'; $data = [ 'actor_id' => $conn->insert_id, 'actor_avatar' => $picture, // used by UI to show avatar 'title' => "User Login by {$first_name} {$last_name}" ]; addNotification(null, $event, $sub_feed, $data, null); header("Location: index.php"); exit(); } else { // echo json_encode(['status' => 'error', 'message' => 'Failed to register user.']); header("Location: index.php"); exit(); } } else { // User exists, set session and redirect $row = $result->fetch_assoc(); $_SESSION['user_id'] = $row['user_id']; $_SESSION['first_name'] = $row['first_name']; $_SESSION['profile_pic'] = $row['profile_pic']; // Send Notification $event = 'user_login'; $sub_feed = 'logins'; $data = [ 'actor_id' => $_SESSION['user_id'], 'actor_avatar' => $_SESSION['profile_pic'], // used by UI to show avatar 'title' => "User Login by {$first_name} {$last_name}" ]; addNotification(null, $event, $sub_feed, $data, null); header("Location: index.php"); exit(); } $stmt->close(); } else { echo "Login failed."; exit(); } } // Check if email and password login is requested if (isset($_POST['email']) && isset($_POST['password'])) { // CSRF Token Validation if (!isset($_POST['csrf_token']) || !validateCSRFToken($_POST['csrf_token'])) { auditLog(null, 'CSRF_VALIDATION_FAILED', 'users', null, ['endpoint' => 'validate_login.php']); echo json_encode(['status' => 'error', 'message' => 'Security token validation failed. Please try again.']); exit(); } // Retrieve and validate email input $email = validateEmail($_POST['email']); if ($email === false) { auditLog(null, 'INVALID_EMAIL_FORMAT', 'users', null, ['email' => $_POST['email']]); echo json_encode(['status' => 'error', 'message' => 'Invalid email format.']); exit(); } // Retrieve and sanitize password $password = isset($_POST['password']) ? trim($_POST['password']) : ''; // Basic validation if (empty($email) || empty($password)) { echo json_encode(['status' => 'error', 'message' => 'Please enter both email and password.']); exit(); } // Check for account lockout $lockoutStatus = checkAccountLockout($email); if ($lockoutStatus['is_locked']) { auditLog(null, 'LOGIN_ATTEMPT_LOCKED_ACCOUNT', 'users', null, [ 'email' => $email, 'locked_until' => $lockoutStatus['locked_until'] ]); echo json_encode([ 'status' => 'error', 'message' => 'Account is temporarily locked due to multiple failed login attempts. Please try again in ' . $lockoutStatus['minutes_remaining'] . ' minutes.' ]); exit(); } // Check recent failed attempts $recentFailedAttempts = countRecentFailedAttempts($email); if ($recentFailedAttempts >= 5) { // Lock account for 15 minutes lockAccount($email, 15); auditLog(null, 'ACCOUNT_LOCKED_THRESHOLD', 'users', null, [ 'email' => $email, 'failed_attempts' => $recentFailedAttempts ]); echo json_encode([ 'status' => 'error', 'message' => 'Account locked due to multiple failed login attempts. Please try again in 15 minutes.' ]); exit(); } // Prepare SQL statement to fetch user details $query = "SELECT * FROM users WHERE email = ?"; $stmt = $conn->prepare($query); if (!$stmt) { echo json_encode(['status' => 'error', 'message' => 'Database query preparation failed.']); exit(); } $stmt->bind_param("s", $email); $stmt->execute(); $result = $stmt->get_result(); // Check if user exists and verify password if ($result->num_rows == 1) { $row = $result->fetch_assoc(); // Check if the user is verified if ($row['is_verified'] == 0) { recordLoginAttempt($email, false); auditLog(null, 'LOGIN_ATTEMPT_UNVERIFIED_ACCOUNT', 'users', $row['user_id']); echo json_encode(['status' => 'error', 'message' => 'Your account is not verified. Please check your email for the verification link.']); exit(); } if (password_verify($password, $row['password'])) { // Record successful attempt recordLoginAttempt($email, true); // Regenerate session ID to prevent session fixation attacks session_regenerate_id(true); // Password is correct, set up session $_SESSION['user_id'] = $row['user_id']; $_SESSION['first_name'] = $row['first_name']; $_SESSION['profile_pic'] = $row['profile_pic']; // Set session timeout (30 minutes) $_SESSION['login_time'] = time(); $_SESSION['session_timeout'] = 1800; // 30 minutes in seconds // Send Notification $event = 'user_login'; $sub_feed = 'logins'; $data = [ 'actor_id' => $_SESSION['user_id'], 'actor_avatar' => $_SESSION['profile_pic'], // used by UI to show avatar 'title' => "User Login by {$first_name} {$last_name}" ]; addNotification(null, $event, $sub_feed, $data, null); auditLog($row['user_id'], 'LOGIN_SUCCESS', 'users', $row['user_id']); echo json_encode(['status' => 'success', 'message' => 'Successful Login']); } else { // Password is incorrect - record failed attempt recordLoginAttempt($email, false); auditLog(null, 'LOGIN_FAILED_INVALID_PASSWORD', 'users', null, ['email' => $email]); // Check if this was the threshold failure $newFailureCount = countRecentFailedAttempts($email); if ($newFailureCount >= 5) { lockAccount($email, 15); echo json_encode([ 'status' => 'error', 'message' => 'Too many failed login attempts. Account locked for 15 minutes.' ]); } else { $attemptsRemaining = 5 - $newFailureCount; echo json_encode([ 'status' => 'error', 'message' => 'Invalid password. ' . $attemptsRemaining . ' attempts remaining before account lockout.' ]); } } } else { // User does not exist - still record attempt recordLoginAttempt($email, false); auditLog(null, 'LOGIN_FAILED_USER_NOT_FOUND', 'users', null, ['email' => $email]); echo json_encode(['status' => 'error', 'message' => 'User with that email does not exist.']); } // Close the statement and connection $stmt->close(); } // Close connection $conn->close(); exit(); ?>