diff --git a/login.php b/login.php
index 2db04167..afbb4e8b 100644
--- a/login.php
+++ b/login.php
@@ -1,5 +1,7 @@
(NOW() - INTERVAL 10 MINUTE)"));
-
+// Block access if more than 15 failed login attempts have happened in the last 10 minutes
+$row = mysqli_fetch_assoc(mysqli_query(
+ $mysqli,
+ "SELECT COUNT(log_id) AS failed_login_count
+ FROM logs
+ WHERE log_ip = '$session_ip'
+ AND log_type = 'Login'
+ AND log_action = 'Failed'
+ AND log_created_at > (NOW() - INTERVAL 10 MINUTE)"
+));
$failed_login_count = intval($row['failed_login_count']);
if ($failed_login_count >= 15) {
@@ -53,253 +72,391 @@ if ($failed_login_count >= 15) {
}
// Query Settings for company
-$sql_settings = mysqli_query($mysqli, "SELECT * FROM settings LEFT JOIN companies ON settings.company_id = companies.company_id WHERE settings.company_id = 1");
+$sql_settings = mysqli_query($mysqli, "
+ SELECT settings.*, companies.company_name, companies.company_logo
+ FROM settings
+ LEFT JOIN companies ON settings.company_id = companies.company_id
+ WHERE settings.company_id = 1
+");
$row = mysqli_fetch_array($sql_settings);
// Company info
-$company_name = $row['company_name'];
-$company_logo = $row['company_logo'];
-$config_start_page = nullable_htmlentities($row['config_start_page']);
-$config_login_message = nullable_htmlentities($row['config_login_message']);
+$company_name = $row['company_name'];
+$company_logo = $row['company_logo'];
+$config_start_page = nullable_htmlentities($row['config_start_page']);
+$config_login_message = nullable_htmlentities($row['config_login_message']);
// Mail
-$config_smtp_host = $row['config_smtp_host'];
-$config_smtp_port = intval($row['config_smtp_port']);
+$config_smtp_host = $row['config_smtp_host'];
+$config_smtp_port = intval($row['config_smtp_port']);
$config_smtp_encryption = $row['config_smtp_encryption'];
-$config_smtp_username = $row['config_smtp_username'];
-$config_smtp_password = $row['config_smtp_password'];
+$config_smtp_username = $row['config_smtp_username'];
+$config_smtp_password = $row['config_smtp_password'];
$config_mail_from_email = sanitizeInput($row['config_mail_from_email']);
-$config_mail_from_name = sanitizeInput($row['config_mail_from_name']);
+$config_mail_from_name = sanitizeInput($row['config_mail_from_name']);
// Client Portal Enabled
-$config_client_portal_enable = intval($row['config_client_portal_enable']);
-
-// Login key (if setup)
-$config_login_key_required = $row['config_login_key_required'];
-$config_login_key_secret = $row['config_login_key_secret'];
-
+$config_client_portal_enable = intval($row['config_client_portal_enable']);
$config_login_remember_me_expire = intval($row['config_login_remember_me_expire']);
-// Login key verification
-// If no/incorrect 'key' is supplied, send to client portal instead
-if ($config_login_key_required) {
- if (!isset($_GET['key']) || $_GET['key'] !== $config_login_key_secret) {
- redirect("client");
- }
-}
+// Azure / Entra for client
+$azure_client_id = $row['config_azure_client_id'] ?? null;
-// HTTP-Only cookies
-ini_set("session.cookie_httponly", true);
+$response = null;
+$token_field = null;
+$show_role_choice = false;
+$email = '';
+$password = '';
-// Tell client to only send cookie(s) over HTTPS
-if ($config_https_only || !isset($config_https_only)) {
- ini_set("session.cookie_secure", true);
-}
+// Handle POST login request (normal login or role choice)
+if ($_SERVER['REQUEST_METHOD'] === 'POST' && (isset($_POST['login']) || isset($_POST['role_choice']))) {
-// Handle POST login request
-if (isset($_POST['login'])) {
-
- // Sessions should start after the user has POSTed data
- session_start();
-
- // Passed login brute force check
- $email = sanitizeInput($_POST['email']);
- $password = $_POST['password'];
-
- $current_code = 0; // Default value
- if (isset($_POST['current_code'])) {
- $current_code = intval($_POST['current_code']);
- }
-
- $row = mysqli_fetch_assoc(mysqli_query($mysqli, "SELECT * FROM users LEFT JOIN user_settings on users.user_id = user_settings.user_id WHERE user_email = '$email' AND user_archived_at IS NULL AND user_status = 1 AND user_type = 1"));
-
- // Check password
- if ($row && password_verify($password, $row['user_password'])) {
-
- // User password correct (partial login)
-
- // Set temporary user variables
- $user_name = sanitizeInput($row['user_name']);
- $user_id = intval($row['user_id']);
- $session_user_id = $user_id; // to pass the user_id to logAction function
- $user_email = sanitizeInput($row['user_email']);
- $token = sanitizeInput($row['user_token']);
- $force_mfa = intval($row['user_config_force_mfa']);
- $user_role_id = intval($row['user_role_id']);
- $user_encryption_ciphertext = $row['user_specific_encryption_ciphertext'];
- $user_extension_key = $row['user_extension_key'];
-
- $mfa_is_complete = false; // Default to requiring MFA
- $extended_log = ''; // Default value
-
- if (empty($token)) {
- // MFA is not configured
- $mfa_is_complete = true;
- }
-
- // Validate MFA via a remember-me cookie
- if (isset($_COOKIE['rememberme'])) {
- // Get remember tokens less than $config_login_remember_me_days_expire days old
- $remember_tokens = mysqli_query($mysqli, "SELECT remember_token_token FROM remember_tokens WHERE remember_token_user_id = $user_id AND remember_token_created_at > (NOW() - INTERVAL $config_login_remember_me_expire DAY)");
- while ($row = mysqli_fetch_assoc($remember_tokens)) {
- if (hash_equals($row['remember_token_token'], $_COOKIE['rememberme'])) {
- $mfa_is_complete = true;
- $extended_log = 'with 2FA remember-me cookie';
- break;
- }
- }
- }
-
- // Validate MFA code
- if (!empty($current_code) && TokenAuth6238::verify($token, $current_code)) {
- $mfa_is_complete = true;
- $extended_log = 'with MFA';
- }
-
- if ($mfa_is_complete) {
- // MFA Completed successfully
-
- // FULL LOGIN SUCCESS
-
- // Create a remember me token, if requested
- if (isset($_POST['remember_me'])) {
- // TODO: Record the UA and IP a token is generated from so that can be shown later on
- $newRememberToken = bin2hex(random_bytes(64));
- setcookie('rememberme', $newRememberToken, time() + 86400*$config_login_remember_me_expire, "/", null, true, true);
- mysqli_query($mysqli, "INSERT INTO remember_tokens SET remember_token_user_id = $user_id, remember_token_token = '$newRememberToken'");
-
- $extended_log .= ", generated a new remember-me token";
- }
-
- // Check this login isn't suspicious
- $sql_ip_prev_logins = mysqli_fetch_assoc(mysqli_query($mysqli, "SELECT COUNT(log_id) AS ip_previous_logins FROM logs WHERE log_type = 'Login' AND log_action = 'Success' AND log_ip = '$session_ip' AND log_user_id = $user_id"));
- $ip_previous_logins = sanitizeInput($sql_ip_prev_logins['ip_previous_logins']);
-
- $sql_ua_prev_logins = mysqli_fetch_assoc(mysqli_query($mysqli, "SELECT COUNT(log_id) AS ua_previous_logins FROM logs WHERE log_type = 'Login' AND log_action = 'Success' AND log_user_agent = '$session_user_agent' AND log_user_id = $user_id"));
- $ua_prev_logins = sanitizeInput($sql_ua_prev_logins['ua_previous_logins']);
-
- // Notify if both the user agent and IP are different
- if (!empty($config_smtp_host) && $ip_previous_logins == 0 && $ua_prev_logins == 0) {
- $subject = "$config_app_name new login for $user_name";
- $body = "Hi $user_name,
A recent successful login to your $config_app_name account was considered a little unusual. If this was you, you can safely ignore this email!
IP Address: $session_ip
User Agent: $session_user_agent
If you did not perform this login, your credentials may be compromised.
Thanks,
ITFlow";
-
- $data = [
- [
- 'from' => $config_mail_from_email,
- 'from_name' => $config_mail_from_name,
- 'recipient' => $user_email,
- 'recipient_name' => $user_name,
- 'subject' => $subject,
- 'body' => $body
- ]
- ];
- addToMailQueue($data);
- }
-
- logAction("Login", "Success", "$user_name successfully logged in $extended_log", 0, $user_id);
-
- // Session info
- $_SESSION['user_id'] = $user_id;
- $_SESSION['csrf_token'] = randomString(156);
- $_SESSION['logged'] = true;
-
- // Forcing MFA
- if ($force_mfa == 1 && $token == NULL) {
- $config_start_page = "user/mfa_enforcement.php";
- }
-
- // Setup encryption session key
- if (isset($user_encryption_ciphertext)) {
- $site_encryption_master_key = decryptUserSpecificKey($user_encryption_ciphertext, $password);
- generateUserSessionKey($site_encryption_master_key);
-
- // Setup extension - currently unused
- //if (is_null($user_extension_key)) {
- // Extension cookie
- // Note: Browsers don't accept cookies with SameSite None if they are not HTTPS.
- //setcookie("user_extension_key", "$user_extension_key", ['path' => '/', 'secure' => true, 'httponly' => true, 'samesite' => 'None']);
-
- // Set PHP session in DB, so we can access the session encryption data (above)
- //$user_php_session = session_id();
- //mysqli_query($mysqli, "UPDATE users SET user_php_session = '$user_php_session' WHERE user_id = $user_id");
- //}
-
- }
-
- // Redirect to last visited or config home
-
- if (isset($_GET['last_visited']) && (str_starts_with(base64_decode($_GET['last_visited']), '/agent') || str_starts_with(base64_decode($_GET['last_visited']), '/admin'))) {
-
- redirect($_SERVER["REQUEST_SCHEME"] . "://" . $config_base_url . base64_decode($_GET['last_visited']) );
-
- } else {
- redirect("agent/$config_start_page");
- }
- } else {
-
- // MFA is configured and needs to be confirmed, or was unsuccessful
-
- // HTML code for the token input field
- $token_field = "
-