| 
									
										
										
										
											2024-06-28 17:05:32 +00:00
										 |  |  | <?php | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-11-28 14:40:50 +00:00
										 |  |  | /** | 
					
						
							| 
									
										
										
										
											2024-11-29 16:08:56 +00:00
										 |  |  |  * User login | 
					
						
							| 
									
										
										
										
											2024-11-28 14:40:50 +00:00
										 |  |  |  * | 
					
						
							| 
									
										
										
										
											2024-11-28 14:49:47 +00:00
										 |  |  |  * This page ("login") handles user login, session management, cookie handling, and error logging. | 
					
						
							| 
									
										
										
										
											2025-04-08 07:30:18 +00:00
										 |  |  |  * Supports "remember me" functionality to extend session duration and two-factor authentication. | 
					
						
							| 
									
										
										
										
											2024-11-28 14:40:50 +00:00
										 |  |  |  * | 
					
						
							|  |  |  |  * Actions Performed: | 
					
						
							| 
									
										
										
										
											2025-04-08 07:30:18 +00:00
										 |  |  |  * - Validates login credentials | 
					
						
							|  |  |  |  * - Handles two-factor authentication if enabled | 
					
						
							|  |  |  |  * - Manages session and cookies based on "remember me" option | 
					
						
							|  |  |  |  * - Logs successful and failed login attempts | 
					
						
							|  |  |  |  * - Displays login form and optional custom messages | 
					
						
							| 
									
										
										
										
											2024-11-28 14:40:50 +00:00
										 |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-07-01 09:45:07 +00:00
										 |  |  | // clear the global error var before login
 | 
					
						
							| 
									
										
										
										
											2024-06-28 17:05:32 +00:00
										 |  |  | unset($error); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | try { | 
					
						
							| 
									
										
										
										
											2024-08-10 18:42:44 +00:00
										 |  |  |     // connect to database
 | 
					
						
							| 
									
										
										
										
											2025-04-08 09:12:14 +00:00
										 |  |  |     $db = connectDB($config)['db']; | 
					
						
							| 
									
										
										
										
											2024-08-10 18:42:44 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-01-06 09:13:28 +00:00
										 |  |  |     // Initialize RateLimiter
 | 
					
						
							|  |  |  |     require_once '../app/classes/ratelimiter.php'; | 
					
						
							| 
									
										
										
										
											2025-04-08 09:12:14 +00:00
										 |  |  |     $rateLimiter = new RateLimiter($db); | 
					
						
							| 
									
										
										
										
											2025-01-06 09:13:28 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-02-23 17:22:47 +00:00
										 |  |  |     // Get user IP
 | 
					
						
							|  |  |  |     $user_IP = getUserIP(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-04-08 07:30:18 +00:00
										 |  |  |     $action = $_REQUEST['action'] ?? ''; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if ($action === 'verify' && isset($_SESSION['2fa_pending_user_id'])) { | 
					
						
							|  |  |  |         // Handle 2FA verification
 | 
					
						
							|  |  |  |         $code = $_POST['code'] ?? ''; | 
					
						
							| 
									
										
										
										
											2025-04-13 16:34:13 +00:00
										 |  |  |         $pending2FA = Session::get2FAPending(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (!$pending2FA) { | 
					
						
							|  |  |  |             header('Location: ' . htmlspecialchars($app_root) . '?page=login'); | 
					
						
							|  |  |  |             exit(); | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2025-04-08 07:30:18 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |         require_once '../app/classes/twoFactorAuth.php'; | 
					
						
							| 
									
										
										
										
											2025-04-08 09:12:14 +00:00
										 |  |  |         $twoFactorAuth = new TwoFactorAuthentication($db); | 
					
						
							| 
									
										
										
										
											2025-04-08 07:30:18 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-04-13 16:34:13 +00:00
										 |  |  |         if ($twoFactorAuth->verify($pending2FA['user_id'], $code)) { | 
					
						
							| 
									
										
										
										
											2025-04-08 07:30:18 +00:00
										 |  |  |             // Complete login
 | 
					
						
							| 
									
										
										
										
											2025-04-13 16:34:13 +00:00
										 |  |  |             handleSuccessfulLogin($pending2FA['user_id'], $pending2FA['username'], | 
					
						
							|  |  |  |                 $pending2FA['remember_me'], $config, $logObject, $user_IP); | 
					
						
							| 
									
										
										
										
											2025-04-08 07:30:18 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |             // Clean up 2FA session data
 | 
					
						
							| 
									
										
										
										
											2025-04-13 16:34:13 +00:00
										 |  |  |             Session::clear2FAPending(); | 
					
						
							| 
									
										
										
										
											2025-04-08 07:30:18 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |             exit(); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // If we get here (and we have code submitted), verification failed
 | 
					
						
							|  |  |  |         if (!empty($code)) { | 
					
						
							|  |  |  |             Feedback::flash('ERROR', 'DEFAULT', 'Invalid verification code'); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // Get any new feedback messages
 | 
					
						
							|  |  |  |         include '../app/helpers/feedback.php'; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-04-14 07:39:58 +00:00
										 |  |  |         // Make userId available to template
 | 
					
						
							|  |  |  |         $userId = $pending2FA['user_id']; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-04-08 07:30:18 +00:00
										 |  |  |         // Load the 2FA verification template
 | 
					
						
							|  |  |  |         include '../app/templates/credentials-2fa-verify.php'; | 
					
						
							|  |  |  |         exit(); | 
					
						
							| 
									
										
										
										
											2025-04-08 09:12:14 +00:00
										 |  |  |     } elseif ($action === 'forgot') { | 
					
						
							|  |  |  |         if ($_SERVER['REQUEST_METHOD'] === 'POST') { | 
					
						
							|  |  |  |             // Handle password reset request
 | 
					
						
							|  |  |  |             try { | 
					
						
							|  |  |  |                 // Validate CSRF token
 | 
					
						
							|  |  |  |                 $security = SecurityHelper::getInstance(); | 
					
						
							|  |  |  |                 if (!$security->verifyCsrfToken($_POST['csrf_token'] ?? '')) { | 
					
						
							|  |  |  |                     throw new Exception('Invalid security token. Please try again.'); | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 // Apply rate limiting
 | 
					
						
							|  |  |  |                 if (!$rateLimiter->isIpWhitelisted($user_IP)) { | 
					
						
							|  |  |  |                     if ($rateLimiter->isIpBlacklisted($user_IP)) { | 
					
						
							|  |  |  |                         throw new Exception(Feedback::get('LOGIN', 'IP_BLACKLISTED')['message']); | 
					
						
							|  |  |  |                     } | 
					
						
							|  |  |  |                     if ($rateLimiter->tooManyAttempts('password_reset', $user_IP)) { | 
					
						
							|  |  |  |                         throw new Exception(Feedback::get('LOGIN', 'TOO_MANY_ATTEMPTS')['message']); | 
					
						
							|  |  |  |                     } | 
					
						
							|  |  |  |                     $rateLimiter->attempt('password_reset', $user_IP); | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 // Validate email
 | 
					
						
							|  |  |  |                 $email = filter_var($_POST['email'] ?? '', FILTER_VALIDATE_EMAIL); | 
					
						
							|  |  |  |                 if (!$email) { | 
					
						
							|  |  |  |                     throw new Exception('Please enter a valid email address.'); | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 // Process reset request
 | 
					
						
							|  |  |  |                 require_once '../app/classes/passwordReset.php'; | 
					
						
							|  |  |  |                 $resetHandler = new PasswordReset($db); | 
					
						
							|  |  |  |                 $result = $resetHandler->requestReset($email); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 // Always show same message whether email exists or not for security
 | 
					
						
							|  |  |  |                 Feedback::flash('NOTICE', 'DEFAULT', $result['message']); | 
					
						
							|  |  |  |                 header("Location: $app_root?page=login"); | 
					
						
							|  |  |  |                 exit(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             } catch (Exception $e) { | 
					
						
							|  |  |  |                 Feedback::flash('ERROR', 'DEFAULT', $e->getMessage()); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // Generate CSRF token
 | 
					
						
							|  |  |  |         $security = SecurityHelper::getInstance(); | 
					
						
							|  |  |  |         $security->generateCsrfToken(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // Load the forgot password form
 | 
					
						
							|  |  |  |         include '../app/helpers/feedback.php'; | 
					
						
							|  |  |  |         include '../app/templates/form-password-forgot.php'; | 
					
						
							|  |  |  |         exit(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     } elseif ($action === 'reset' && isset($_GET['token'])) { | 
					
						
							|  |  |  |         // Handle password reset
 | 
					
						
							|  |  |  |         try { | 
					
						
							|  |  |  |             require_once '../app/classes/passwordReset.php'; | 
					
						
							|  |  |  |             $resetHandler = new PasswordReset($db); | 
					
						
							|  |  |  |             $token = $_GET['token']; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if ($_SERVER['REQUEST_METHOD'] === 'POST') { | 
					
						
							|  |  |  |                 // Validate CSRF token
 | 
					
						
							|  |  |  |                 $security = SecurityHelper::getInstance(); | 
					
						
							|  |  |  |                 if (!$security->verifyCsrfToken($_POST['csrf_token'] ?? '')) { | 
					
						
							|  |  |  |                     throw new Exception('Invalid security token. Please try again.'); | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 // Apply rate limiting
 | 
					
						
							|  |  |  |                 if (!$rateLimiter->isIpWhitelisted($user_IP)) { | 
					
						
							|  |  |  |                     if ($rateLimiter->tooManyAttempts('password_reset', $user_IP)) { | 
					
						
							|  |  |  |                         throw new Exception(Feedback::get('LOGIN', 'TOO_MANY_ATTEMPTS')['message']); | 
					
						
							|  |  |  |                     } | 
					
						
							|  |  |  |                     $rateLimiter->attempt('password_reset', $user_IP); | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 // Validate password
 | 
					
						
							|  |  |  |                 require_once '../app/classes/validator.php'; | 
					
						
							|  |  |  |                 $validator = new Validator($_POST); | 
					
						
							|  |  |  |                 $rules = [ | 
					
						
							|  |  |  |                     'new_password' => [ | 
					
						
							|  |  |  |                         'required' => true, | 
					
						
							|  |  |  |                         'min' => 8 | 
					
						
							|  |  |  |                     ], | 
					
						
							|  |  |  |                     'confirm_password' => [ | 
					
						
							|  |  |  |                         'required' => true, | 
					
						
							|  |  |  |                         'matches' => 'new_password' | 
					
						
							|  |  |  |                     ] | 
					
						
							|  |  |  |                 ]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 if (!$validator->validate($rules)) { | 
					
						
							|  |  |  |                     throw new Exception($validator->getFirstError()); | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 // Reset password
 | 
					
						
							|  |  |  |                 if ($resetHandler->resetPassword($token, $_POST['new_password'])) { | 
					
						
							|  |  |  |                     Feedback::flash('NOTICE', 'DEFAULT', 'Your password has been reset successfully. You can now log in.'); | 
					
						
							|  |  |  |                     header("Location: $app_root?page=login"); | 
					
						
							|  |  |  |                     exit(); | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |                 throw new Exception('Invalid or expired reset link. Please request a new one.'); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             // Verify token is valid
 | 
					
						
							|  |  |  |             $validation = $resetHandler->validateToken($token); | 
					
						
							|  |  |  |             if (!$validation['valid']) { | 
					
						
							|  |  |  |                 throw new Exception('Invalid or expired reset link. Please request a new one.'); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             // Show reset password form
 | 
					
						
							|  |  |  |             include '../app/helpers/feedback.php'; | 
					
						
							|  |  |  |             include '../app/templates/form-password-reset.php'; | 
					
						
							|  |  |  |             exit(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         } catch (Exception $e) { | 
					
						
							|  |  |  |             Feedback::flash('ERROR', 'DEFAULT', $e->getMessage()); | 
					
						
							|  |  |  |             header("Location: $app_root?page=login&action=forgot"); | 
					
						
							|  |  |  |             exit(); | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2025-04-08 07:30:18 +00:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if ( $_SERVER['REQUEST_METHOD'] == 'POST' && $action !== 'verify' ) { | 
					
						
							| 
									
										
										
										
											2024-12-07 13:38:13 +00:00
										 |  |  |         try { | 
					
						
							| 
									
										
										
										
											2025-01-30 16:48:46 +00:00
										 |  |  |             // Validate form data
 | 
					
						
							|  |  |  |             $security = SecurityHelper::getInstance(); | 
					
						
							|  |  |  |             $formData = $security->sanitizeArray($_POST, ['username', 'password', 'remember_me', 'csrf_token']); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             $validationRules = [ | 
					
						
							|  |  |  |                 'username' => [ | 
					
						
							|  |  |  |                     'type' => 'string', | 
					
						
							|  |  |  |                     'required' => true, | 
					
						
							|  |  |  |                     'min' => 3, | 
					
						
							|  |  |  |                     'max' => 20 | 
					
						
							|  |  |  |                 ], | 
					
						
							|  |  |  |                 'password' => [ | 
					
						
							|  |  |  |                     'type' => 'string', | 
					
						
							| 
									
										
										
										
											2025-04-14 16:12:26 +00:00
										 |  |  |                     'required' => true | 
					
						
							| 
									
										
										
										
											2025-01-30 16:48:46 +00:00
										 |  |  |                 ] | 
					
						
							|  |  |  |             ]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             $errors = $security->validateFormData($formData, $validationRules); | 
					
						
							|  |  |  |             if (!empty($errors)) { | 
					
						
							|  |  |  |                 throw new Exception("Invalid input: " . implode(", ", $errors)); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-02-21 09:44:52 +00:00
										 |  |  |             $username = $formData['username']; | 
					
						
							|  |  |  |             $password = $formData['password']; | 
					
						
							| 
									
										
										
										
											2024-06-28 17:05:32 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-02-23 17:22:47 +00:00
										 |  |  |             // Skip all checks if IP is whitelisted
 | 
					
						
							| 
									
										
										
										
											2025-01-06 09:13:28 +00:00
										 |  |  |             if (!$rateLimiter->isIpWhitelisted($user_IP)) { | 
					
						
							| 
									
										
										
										
											2025-02-23 17:22:47 +00:00
										 |  |  |                 // Check if IP is blacklisted
 | 
					
						
							|  |  |  |                 if ($rateLimiter->isIpBlacklisted($user_IP)) { | 
					
						
							|  |  |  |                     throw new Exception(Feedback::get('LOGIN', 'IP_BLACKLISTED')['message']); | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 // Check rate limiting before recording attempt
 | 
					
						
							|  |  |  |                 if ($rateLimiter->tooManyAttempts($username, $user_IP)) { | 
					
						
							| 
									
										
										
										
											2025-02-24 12:08:05 +00:00
										 |  |  |                     throw new Exception(Feedback::get('LOGIN', 'TOO_MANY_ATTEMPTS')['message']); | 
					
						
							| 
									
										
										
										
											2025-01-06 09:13:28 +00:00
										 |  |  |                 } | 
					
						
							| 
									
										
										
										
											2025-02-23 17:22:47 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-04-14 16:12:26 +00:00
										 |  |  |                 // Record this attempt before trying to login
 | 
					
						
							|  |  |  |                 $rateLimiter->attempt($username, $user_IP, false); | 
					
						
							| 
									
										
										
										
											2025-01-06 09:13:28 +00:00
										 |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-04-08 07:30:18 +00:00
										 |  |  |             // Attempt login
 | 
					
						
							|  |  |  |             $loginResult = $userObject->login($username, $password); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if (is_array($loginResult)) { | 
					
						
							|  |  |  |                 switch ($loginResult['status']) { | 
					
						
							|  |  |  |                     case 'requires_2fa': | 
					
						
							|  |  |  |                         // Store pending 2FA info
 | 
					
						
							| 
									
										
										
										
											2025-04-13 16:34:13 +00:00
										 |  |  |                         Session::store2FAPending($loginResult['user_id'], $loginResult['username'], | 
					
						
							|  |  |  |                             isset($formData['remember_me'])); | 
					
						
							| 
									
										
										
										
											2025-04-08 07:30:18 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |                         // Redirect to 2FA verification
 | 
					
						
							|  |  |  |                         header('Location: ?page=login&action=verify'); | 
					
						
							|  |  |  |                         exit(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                     case 'success': | 
					
						
							|  |  |  |                         // Complete login
 | 
					
						
							|  |  |  |                         handleSuccessfulLogin($loginResult['user_id'], $loginResult['username'], | 
					
						
							|  |  |  |                             isset($formData['remember_me']), $config, $logObject, $user_IP); | 
					
						
							|  |  |  |                         exit(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                     default: | 
					
						
							|  |  |  |                         throw new Exception($loginResult['message'] ?? 'Login failed'); | 
					
						
							| 
									
										
										
										
											2024-12-07 13:38:13 +00:00
										 |  |  |                 } | 
					
						
							|  |  |  |             } | 
					
						
							| 
									
										
										
										
											2025-04-08 07:30:18 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |             throw new Exception(Feedback::get('LOGIN', 'LOGIN_FAILED')['message']); | 
					
						
							| 
									
										
										
										
											2024-12-07 13:38:13 +00:00
										 |  |  |         } catch (Exception $e) { | 
					
						
							| 
									
										
										
										
											2025-01-04 09:46:07 +00:00
										 |  |  |             // Log the failed attempt
 | 
					
						
							| 
									
										
										
										
											2025-02-16 08:18:26 +00:00
										 |  |  |             Feedback::flash('ERROR', 'DEFAULT', $e->getMessage()); | 
					
						
							| 
									
										
										
										
											2025-01-04 09:46:07 +00:00
										 |  |  |             if (isset($username)) { | 
					
						
							| 
									
										
										
										
											2025-04-14 07:39:58 +00:00
										 |  |  |                 $userId = $userObject->getUserId($username)[0]['id'] ?? 0; | 
					
						
							|  |  |  |                 $logObject->insertLog($userId, "Login: Failed login attempt for user \"$username\". IP: $user_IP. Reason: {$e->getMessage()}", 'user');
 | 
					
						
							| 
									
										
										
										
											2025-04-14 16:12:26 +00:00
										 |  |  |                 $rateLimiter->attempt($username, $user_IP); | 
					
						
							| 
									
										
										
										
											2025-01-04 09:46:07 +00:00
										 |  |  |             } | 
					
						
							| 
									
										
										
										
											2024-06-28 17:05:32 +00:00
										 |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } catch (Exception $e) { | 
					
						
							| 
									
										
										
										
											2025-02-24 12:08:05 +00:00
										 |  |  |     Feedback::flash('ERROR', 'DEFAULT'); | 
					
						
							| 
									
										
										
										
											2024-06-28 17:05:32 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-01-06 09:13:28 +00:00
										 |  |  | // Show configured login message if any
 | 
					
						
							| 
									
										
										
										
											2024-08-01 08:12:54 +00:00
										 |  |  | if (!empty($config['login_message'])) { | 
					
						
							| 
									
										
										
										
											2025-04-14 12:31:19 +00:00
										 |  |  |     echo Feedback::render('NOTICE', 'DEFAULT', $config['login_message'], false, false, false); | 
					
						
							| 
									
										
										
										
											2024-08-01 08:12:54 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-02-17 08:24:50 +00:00
										 |  |  | // Get any new feedback messages
 | 
					
						
							| 
									
										
										
										
											2025-02-17 14:50:57 +00:00
										 |  |  | include '../app/helpers/feedback.php'; | 
					
						
							| 
									
										
										
										
											2025-01-06 09:13:28 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | // Load the template
 | 
					
						
							| 
									
										
										
										
											2024-08-12 11:12:24 +00:00
										 |  |  | include '../app/templates/form-login.php'; | 
					
						
							| 
									
										
										
										
											2025-04-08 07:30:18 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | /** | 
					
						
							|  |  |  |  * Handle successful login by setting up session and cookies | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | function handleSuccessfulLogin($userId, $username, $rememberMe, $config, $logObject, $userIP) { | 
					
						
							| 
									
										
										
										
											2025-04-13 16:34:13 +00:00
										 |  |  |     // Create authenticated session
 | 
					
						
							|  |  |  |     Session::createAuthSession($userId, $username, $rememberMe, $config); | 
					
						
							| 
									
										
										
										
											2025-04-08 07:30:18 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     // Log successful login
 | 
					
						
							|  |  |  |     $logObject->insertLog($userId, "Login: User \"$username\" logged in. IP: $userIP", 'user');
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Set success message and redirect
 | 
					
						
							|  |  |  |     Feedback::flash('LOGIN', 'LOGIN_SUCCESS'); | 
					
						
							|  |  |  |     header('Location: ' . htmlspecialchars($app_root)); | 
					
						
							|  |  |  | } |