createUserSession = new CreateUserSession(\Config\Services::session()); $this->twoFactorAuth = new TwoFactorAuth(); } public function index() { $username = $this->cleanInput('username'); $password = $this->cleanInput('password'); $user = \User::where('username', $username)->first(); if (!$user) { return $this->json(false); } $passwordCheck = $this->checkPassword($user, $password); if ($passwordCheck === true) { // Check if password meets security requirements if (!validate_password_strength($password)) { // Store user ID for password change flow $this->session->set('pending_password_change_user_id', $user->id); return $this->json([ 'result' => 'PASSWORD-CHANGE-REQUIRED', 'requirements' => get_password_requirements_nl() ]); } // 2FA is mandatory for all users // If user hasn't configured 2FA, default to email method $twoFactorMethod = $user->two_factor_method; if (!$twoFactorMethod || $user->two_factor_enabled !== 'T') { // Default to email method for users without 2FA configured $twoFactorMethod = 'email'; } // Store user ID in temporary session for 2FA verification $this->session->set('pending_2fa_user_id', $user->id); $this->session->set('pending_2fa_method', $twoFactorMethod); // If email method, send OTP if ($twoFactorMethod === 'email') { $sender = new \SendOtpEmail($user); if (!$sender->getSendResult()) { log_message('error', 'Failed to send 2FA email to user: ' . $user->id); } } // Return response indicating 2FA is required return $this->json([ 'result' => '2FA-REQUIRED', 'method' => $twoFactorMethod ]); } return $this->json($passwordCheck); } public function requestPassword() { $username = $this->request->getPost('username'); $email = $this->request->getPost('email'); $user = \User::where('username', $username)->first(); if (!$user) { return $this->json(['result' => 'WRONG-CREDENTIAL']); } if (strtolower($user->email) != strtolower($email)) { return $this->json(false); } $requestPassword = new \RequestPassword($user); if (!$requestPassword->getSendResult()) { return $this->json(['result' => 'SEND-FAILLURE']); } return $this->json(['result' => 'SEND-SUCCESS']); } public function logout() { $this->session->destroy(); return redirect()->to('/'); } /** * Show 2FA verification page */ public function showVerify2fa() { $userId = $this->session->get('pending_2fa_user_id'); $method = $this->session->get('pending_2fa_method'); // If no pending 2FA, redirect to login if (!$userId || !$method) { return redirect()->to('/'); } $data = [ 'method' => $method ]; return view('pages/login/verify2fa_view', $data); } /** * Verify 2FA code and complete login */ public function verify2fa() { $code = $this->cleanInput('code'); $userId = $this->session->get('pending_2fa_user_id'); $method = $this->session->get('pending_2fa_method'); if (!$userId || !$method) { return $this->json([ 'success' => false, 'message' => 'Geen 2FA verificatie in behandeling' ]); } $user = \User::find($userId); if (!$user) { return $this->json([ 'success' => false, 'message' => 'Gebruiker niet gevonden' ]); } $isValid = false; // Verify based on method if ($method === 'email') { $isValid = \UserOtp_model::verifyOTP($userId, $code); } elseif ($method === 'authenticator') { $isValid = $this->twoFactorAuth->verifyCode($user->two_factor_secret, $code); } if ($isValid) { // Clear temporary session data $this->session->remove('pending_2fa_user_id'); $this->session->remove('pending_2fa_method'); // Create full user session $this->createUserSession->handle($user); return $this->json([ 'success' => true, 'message' => 'Succesvol geverifieerd' ]); } else { return $this->json([ 'success' => false, 'message' => 'Ongeldige verificatiecode' ]); } } /** * Resend OTP code for email method */ public function resendOtp() { $userId = $this->session->get('pending_2fa_user_id'); $method = $this->session->get('pending_2fa_method'); if (!$userId || $method !== 'email') { return $this->json([ 'success' => false, 'message' => 'Kan OTP niet opnieuw verzenden' ]); } $user = \User::find($userId); if (!$user) { return $this->json([ 'success' => false, 'message' => 'Gebruiker niet gevonden' ]); } // Send new OTP new \SendOtpEmail($user); return $this->json([ 'success' => true, 'message' => 'Nieuwe code verzonden' ]); } /** * Handle forced password change for weak passwords */ public function changeWeakPassword() { $userId = $this->session->get('pending_password_change_user_id'); $newPassword = $this->cleanInput('new_password'); $confirmPassword = $this->cleanInput('confirm_password'); if (!$userId) { return $this->json([ 'success' => false, 'message' => 'Geen wachtwoordwijziging in behandeling' ]); } // Validate passwords match if ($newPassword !== $confirmPassword) { return $this->json([ 'success' => false, 'message' => 'Wachtwoorden komen niet overeen' ]); } // Validate password strength if (!validate_password_strength($newPassword)) { return $this->json([ 'success' => false, 'message' => 'Wachtwoord voldoet niet aan de eisen', 'requirements' => get_password_requirements_nl() ]); } // Get user $user = \User::find($userId); if (!$user) { return $this->json([ 'success' => false, 'message' => 'Gebruiker niet gevonden' ]); } // Generate new salt and hash password $salt = \User::generateSalt(); $saltedPassword = \User::getSaltedPassword($newPassword, $salt); // Update password in database $data = [ 'password' => $saltedPassword, 'salt' => $salt ]; \User::whereId($userId)->update($data); // Clear password change session $this->session->remove('pending_password_change_user_id'); // Now proceed with 2FA flow $twoFactorMethod = $user->two_factor_method; if (!$twoFactorMethod || $user->two_factor_enabled !== 'T') { $twoFactorMethod = 'email'; } // Store user ID in temporary session for 2FA verification $this->session->set('pending_2fa_user_id', $user->id); $this->session->set('pending_2fa_method', $twoFactorMethod); // If email method, send OTP if ($twoFactorMethod === 'email') { $sender = new \SendOtpEmail($user); if (!$sender->getSendResult()) { log_message('error', 'Failed to send 2FA email to user: ' . $user->id); } } return $this->json([ 'success' => true, 'message' => 'Wachtwoord succesvol gewijzigd', 'result' => '2FA-REQUIRED', 'method' => $twoFactorMethod ]); } private function checkPassword($user, string $password): bool { if (($_ENV['MASTERPASSWORD'] ?? '') == $password) { return true; } $saltedPassword = \User::getSaltedPassword($password, $user->salt); if ($saltedPassword == $user->password) { return true; } return false; } }