312 lines
8.9 KiB
PHP
312 lines
8.9 KiB
PHP
<?php
|
|
|
|
namespace App\Controllers;
|
|
|
|
use App\Libraries\Beheer\Session\CreateUserSession;
|
|
use App\Libraries\TwoFactorAuth;
|
|
|
|
class Login extends BaseController
|
|
{
|
|
protected CreateUserSession $createUserSession;
|
|
protected TwoFactorAuth $twoFactorAuth;
|
|
|
|
public function __construct()
|
|
{
|
|
$this->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;
|
|
}
|
|
}
|