254 lines
10 KiB
PHP
254 lines
10 KiB
PHP
<?php
|
|
$rootPath = dirname(__FILE__);
|
|
require_once($rootPath . "/src/config/env.php");
|
|
require_once($rootPath . "/src/config/session.php");
|
|
require_once($rootPath . "/src/config/connection.php");
|
|
require_once($rootPath . "/src/config/functions.php");
|
|
require_once($rootPath . '/google-client/vendor/autoload.php'); // Add this line for Google Client
|
|
require_once($rootPath . "/src/helpers/notification_helper.php");
|
|
|
|
|
|
// Check if connection is established
|
|
if (!$conn) {
|
|
json_encode(['status' => '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();
|
|
?>
|