Adds 2fa to the login page

main
Yasen Pramatarov 2025-04-08 10:30:18 +03:00
parent eb0a603b8d
commit 71b0448004
1 changed files with 115 additions and 51 deletions

View File

@ -4,20 +4,20 @@
* User login * User login
* *
* This page ("login") handles user login, session management, cookie handling, and error logging. * This page ("login") handles user login, session management, cookie handling, and error logging.
* Supports "remember me" functionality to extend session duration. * Supports "remember me" functionality to extend session duration and two-factor authentication.
* *
* Actions Performed: * Actions Performed:
* - Validates login credentials. * - Validates login credentials
* - Manages session and cookies based on "remember me" option. * - Handles two-factor authentication if enabled
* - Logs successful and failed login attempts. * - Manages session and cookies based on "remember me" option
* - Displays login form and optional custom messages. * - Logs successful and failed login attempts
* - Displays login form and optional custom messages
*/ */
// clear the global error var before login // clear the global error var before login
unset($error); unset($error);
try { try {
// connect to database // connect to database
$dbWeb = connectDB($config)['db']; $dbWeb = connectDB($config)['db'];
@ -28,7 +28,44 @@ try {
// Get user IP // Get user IP
$user_IP = getUserIP(); $user_IP = getUserIP();
if ( $_SERVER['REQUEST_METHOD'] == 'POST' ) { $action = $_REQUEST['action'] ?? '';
if ($action === 'verify' && isset($_SESSION['2fa_pending_user_id'])) {
// Handle 2FA verification
$code = $_POST['code'] ?? '';
$userId = $_SESSION['2fa_pending_user_id'];
$username = $_SESSION['2fa_pending_username'];
$rememberMe = isset($_SESSION['2fa_pending_remember']);
require_once '../app/classes/twoFactorAuth.php';
$twoFactorAuth = new TwoFactorAuthentication($dbWeb);
if ($twoFactorAuth->verify($userId, $code)) {
// Complete login
handleSuccessfulLogin($userId, $username, $rememberMe, $config, $logObject, $user_IP);
// Clean up 2FA session data
unset($_SESSION['2fa_pending_user_id']);
unset($_SESSION['2fa_pending_username']);
unset($_SESSION['2fa_pending_remember']);
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';
// Load the 2FA verification template
include '../app/templates/credentials-2fa-verify.php';
exit();
}
if ( $_SERVER['REQUEST_METHOD'] == 'POST' && $action !== 'verify' ) {
try { try {
// Validate form data // Validate form data
$security = SecurityHelper::getInstance(); $security = SecurityHelper::getInstance();
@ -44,7 +81,7 @@ try {
'password' => [ 'password' => [
'type' => 'string', 'type' => 'string',
'required' => true, 'required' => true,
'min' => 2 'min' => 5
] ]
]; ];
@ -72,51 +109,35 @@ try {
$rateLimiter->attempt($username, $user_IP); $rateLimiter->attempt($username, $user_IP);
} }
// login successful // Attempt login
if ( $userObject->login($username, $password) ) { $loginResult = $userObject->login($username, $password);
// if remember_me is checked, max out the session
if (isset($formData['remember_me'])) { if (is_array($loginResult)) {
// 30*24*60*60 = 30 days switch ($loginResult['status']) {
$cookie_lifetime = 30 * 24 * 60 * 60; case 'requires_2fa':
$setcookie_lifetime = time() + 30 * 24 * 60 * 60; // Store pending 2FA info
} else { $_SESSION['2fa_pending_user_id'] = $loginResult['user_id'];
// 0 - session end on browser close $_SESSION['2fa_pending_username'] = $loginResult['username'];
$cookie_lifetime = 0; if (isset($formData['remember_me'])) {
$setcookie_lifetime = 0; $_SESSION['2fa_pending_remember'] = true;
}
// 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');
} }
// Regenerate session ID to prevent session fixation
session_regenerate_id(true);
// set session lifetime and cookies
setcookie('username', $username, [
'expires' => $setcookie_lifetime,
'path' => $config['folder'],
'domain' => $config['domain'],
'secure' => isset($_SERVER['HTTPS']),
'httponly' => true,
'samesite' => 'Strict'
]);
// Set session variables
$_SESSION['USER_ID'] = $userObject->getUserId($username)[0]['id'];
$_SESSION['USERNAME'] = $username;
$_SESSION['LAST_ACTIVITY'] = time();
if (isset($formData['remember_me'])) {
$_SESSION['REMEMBER_ME'] = true;
}
// Log successful login
$user_id = $userObject->getUserId($username)[0]['id'];
$logObject->insertLog($user_id, "Login: User \"$username\" logged in. IP: $user_IP", 'user');
// Set success message and redirect
Feedback::flash('LOGIN', 'LOGIN_SUCCESS');
header('Location: ' . htmlspecialchars($app_root));
exit();
} else {
throw new Exception(Feedback::get('LOGIN', 'LOGIN_FAILED')['message']);
} }
throw new Exception(Feedback::get('LOGIN', 'LOGIN_FAILED')['message']);
} catch (Exception $e) { } catch (Exception $e) {
// Log the failed attempt // Log the failed attempt
Feedback::flash('ERROR', 'DEFAULT', $e->getMessage()); Feedback::flash('ERROR', 'DEFAULT', $e->getMessage());
@ -140,3 +161,46 @@ include '../app/helpers/feedback.php';
// Load the template // Load the template
include '../app/templates/form-login.php'; include '../app/templates/form-login.php';
/**
* Handle successful login by setting up session and cookies
*/
function handleSuccessfulLogin($userId, $username, $rememberMe, $config, $logObject, $userIP) {
if ($rememberMe) {
// 30*24*60*60 = 30 days
$cookie_lifetime = 30 * 24 * 60 * 60;
$setcookie_lifetime = time() + 30 * 24 * 60 * 60;
} else {
// 0 - session end on browser close
$cookie_lifetime = 0;
$setcookie_lifetime = 0;
}
// Regenerate session ID to prevent session fixation
session_regenerate_id(true);
// set session lifetime and cookies
setcookie('username', $username, [
'expires' => $setcookie_lifetime,
'path' => $config['folder'],
'domain' => $config['domain'],
'secure' => isset($_SERVER['HTTPS']),
'httponly' => true,
'samesite' => 'Strict'
]);
// Set session variables
$_SESSION['user_id'] = $userId;
$_SESSION['USERNAME'] = $username;
$_SESSION['LAST_ACTIVITY'] = time();
if ($rememberMe) {
$_SESSION['REMEMBER_ME'] = true;
}
// 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));
}