[WIP] Working 2fa email auth

This commit is contained in:
slawkens
2025-06-22 18:50:54 +02:00
parent 96d6e04bd2
commit b3dfc56c96
17 changed files with 513 additions and 195 deletions

View File

@@ -12,33 +12,5 @@ class EmailAuthGateway extends BaseAuthGateway implements AuthGatewayInterface
{
return AccountEMailCode::where('account_id', '=', $this->account->getId())->where('code', $code)->where('created_at', '>', time() - TwoFactorAuth::EMAIL_CODE_VALID_UNTIL)->first() !== null;
}
public function hasRecentEmailCode(): bool {
return AccountEMailCode::where('account_id', '=', $this->account->getId())->where('created_at', '>', time() - TwoFactorAuth::EMAIL_CODE_VALID_UNTIL)->first() !== null;
}
public function deleteOldCodes(): void {
AccountEMailCode::where('account_id', '=', $this->account->getId())->delete();
}
public function resendEmailCode(): void
{
global $twig;
$newCode = generateRandomString(6, true, false, true);
AccountEMailCode::create([
'account_id' => $this->account->getId(),
'code' => $newCode,
'created_at' => time(),
]);
$mailBody = $twig->render('mail.account.2fa.email-code.html.twig', [
'code' => $newCode,
]);
if (!_mail($this->account->getEMail(), configLua('serverName') . ' - Requested Authentication Email Code', $mailBody)) {
error('An error occurred while sending email. For Admin: More info can be found in system/logs/mailer-error.log');
}
}
}

View File

@@ -12,43 +12,126 @@ class TwoFactorAuth
const TYPE_NONE = 0;
const TYPE_EMAIL = 1;
const TYPE_APP = 2;
// maybe later
//const TYPE_SMS = 3;
const EMAIL_CODE_VALID_UNTIL = 24 * 60 * 60;
private static TwoFactorAuth $instance;
private \OTS_Account $account;
private int $authType;
private EmailAuthGateway|AppAuthGateway $authGateway;
public function __construct(\OTS_Account $account) {
$this->account = $account;
public function __construct(\OTS_Account|int $account) {
if (is_int($account)) {
$this->account = new \OTS_Account();
$this->account->load($account);
}
else {
$this->account = $account;
}
$this->authType = (int)$this->account->getCustomField('2fa_type');
if ($this->authType === self::TYPE_EMAIL) {
$this->authGateway = new EmailAuthGateway($account);
}
else if ($this->authType === self::TYPE_APP) {
$this->authGateway = new AppAuthGateway($account);
}
$this->setAuthGateway($this->authType);
}
public function process()
public static function getInstance($account = null): self
{
if (!isset(self::$instance)) {
self::$instance = new self($account);
}
return self::$instance;
}
public function process($code): bool
{
global $twig;
if (!$this->isActive()) {
return true;
}
if (!empty($code)) {
if ($this->getAuthGateway()->verifyCode($code)) {
if ($this->authType === self::TYPE_EMAIL) {
$this->deleteOldCodes();
}
header('Location: account/manage');
return true;
}
else {
if (setting('core.mail_enabled')) {
$mailBody = $twig->render('mail.account.2fa.email-code.wrong-attempt.html.twig');
if (!_mail($this->account->getEMail(), configLua('serverName') . ' - Failed Two-Factor Authentication Attempt', $mailBody)) {
error('An error occurred while sending email. For Admin: More info can be found in system/logs/mailer-error.log');
}
}
define('HIDE_LOGIN_BOX', true);
$errors[] = 'Invalid email code!';
$twig->display('error_box.html.twig', ['errors' => $errors]);
$twig->display('account.2fa.email-code.login.html.twig', ['wrongCode' => true]);
return false;
}
}
if ($this->authType == TwoFactorAuth::TYPE_EMAIL) {
if (!$this->authGateway->hasRecentEmailCode()) {
$this->authGateway->resendEmailCode();
success('Resent email.');
if (!$this->hasRecentEmailCode(15 * 60)) {
$this->resendEmailCode();
//success('Resent email.');
}
define('HIDE_LOGIN_BOX', true);
$twig->display('account.2fa.email-code.login.html.twig');
return false;
}
return true;
}
public function setAuthGateway(int $authType): void
{
if ($authType === self::TYPE_EMAIL) {
$this->authGateway = new EmailAuthGateway($this->account);
}
else if ($authType === self::TYPE_APP) {
$this->authGateway = new AppAuthGateway($this->account);
}
}
public function getAccountManageViews(): array
{
$twoFactorView = 'account.2fa.protected.html.twig';
if ($this->authType == TwoFactorAuth::TYPE_EMAIL) {
$twoFactorView2 = 'account.2fa.email.activated.html.twig';
}
elseif ($this->authType == TwoFactorAuth::TYPE_APP) {
$twoFactorView2 = 'account.2fa.app.activated.html.twig';
}
else {
$twoFactorView = 'account.2fa.connect.html.twig';
$twoFactorView2 = 'account.2fa.email.activate.html.twig';
}
return [$twoFactorView, $twoFactorView2];
}
public function enable(int $type): void {
$this->account->setCustomField('2fa_type', $type);
}
public function disable(): void {
$this->account->setCustomField('2fa_type', self::TYPE_NONE);
}
public function isActive(): bool {
return $this->authType != self::TYPE_NONE;
}
@@ -60,4 +143,32 @@ class TwoFactorAuth
public function getAuthGateway(): AppAuthGateway|EmailAuthGateway {
return $this->authGateway;
}
public function hasRecentEmailCode($since = TwoFactorAuth::EMAIL_CODE_VALID_UNTIL): bool {
return AccountEMailCode::where('account_id', '=', $this->account->getId())->where('created_at', '>', time() - $since)->first() !== null;
}
public function deleteOldCodes(): void {
AccountEMailCode::where('account_id', '=', $this->account->getId())->delete();
}
public function resendEmailCode(): void
{
global $twig;
$newCode = generateRandomString(6, true, false, true);
AccountEMailCode::create([
'account_id' => $this->account->getId(),
'code' => $newCode,
'created_at' => time(),
]);
$mailBody = $twig->render('mail.account.2fa.email-code.html.twig', [
'code' => $newCode,
]);
if (!_mail($this->account->getEMail(), configLua('serverName') . ' - Requested Authentication Email Code', $mailBody)) {
error('An error occurred while sending email. For Admin: More info can be found in system/logs/mailer-error.log');
}
}
}