From b3dfc56c9664d30689ddb4dd8ecfa1ebc069e037 Mon Sep 17 00:00:00 2001 From: slawkens Date: Sun, 22 Jun 2025 18:50:54 +0200 Subject: [PATCH] [WIP] Working 2fa email auth --- system/pages/account/2fa.php | 131 +++++++++++------ system/pages/account/login.php | 4 +- system/pages/account/manage.php | 7 +- .../Gateway/EmailAuthGateway.php | 28 ---- system/src/TwoFactorAuth/TwoFactorAuth.php | 135 ++++++++++++++++-- .../templates/account.2fa.connect.html.twig | 36 +++++ ...ccount.2fa.email-code.deactivate.html.twig | 109 ++++++++++++++ .../account.2fa.email-code.login.html.twig | 36 ++++- .../account.2fa.email-code.success.html.twig | 15 -- .../account.2fa.email.activate.html.twig | 37 +++++ .../account.2fa.email.activated.html.twig | 26 ++++ .../account.2fa.email_code.html.twig | 27 +++- system/templates/account.2fa.main.html.twig | 12 ++ .../templates/account.2fa.protected.html.twig | 18 +++ system/templates/account.management.html.twig | 3 + templates/tibiacom/account.2fa.main.html.twig | 81 ----------- .../tibiacom/account.management.html.twig | 3 + 17 files changed, 513 insertions(+), 195 deletions(-) create mode 100644 system/templates/account.2fa.connect.html.twig create mode 100644 system/templates/account.2fa.email-code.deactivate.html.twig delete mode 100644 system/templates/account.2fa.email-code.success.html.twig create mode 100644 system/templates/account.2fa.email.activate.html.twig create mode 100644 system/templates/account.2fa.email.activated.html.twig rename {templates/tibiacom => system/templates}/account.2fa.email_code.html.twig (78%) create mode 100644 system/templates/account.2fa.main.html.twig create mode 100644 system/templates/account.2fa.protected.html.twig delete mode 100644 templates/tibiacom/account.2fa.main.html.twig diff --git a/system/pages/account/2fa.php b/system/pages/account/2fa.php index 3d060a17..fca2bcdb 100644 --- a/system/pages/account/2fa.php +++ b/system/pages/account/2fa.php @@ -20,61 +20,102 @@ csrfProtect(); /** * @var OTS_Account $account_logged */ -$step = isset($_REQUEST['step']) ?? ''; +$step = $_REQUEST['step'] ?? ''; +$code = $_REQUEST['email-code'] ?? ''; if ((!setting('core.mail_enabled')) && ACTION == 'email-code') { $twig->display('error_box.html.twig', ['errors' => ['Account two-factor e-mail authentication disabled.']]); return; } -$twoFactorAuth = new TwoFactorAuth($account_logged); +if (!isset($account_logged) || !$account_logged->isLoaded()) { + $current_session = getSession('account'); + if($current_session) { + $account_logged->load($current_session); + } +} + +$twoFactorAuth = TwoFactorAuth::getInstance($account_logged); if (ACTION == 'email-code') { - if ($step === 'verify') { - $code = $_POST['email-code'] ?? ''; - if ($twoFactorAuth->getAuthGateway()->verifyCode($code)) { - $twoFactorAuth->getAuthGateway()->deleteOldCodes(); - - //session(['2fa_skip' => true]); - header('Location: account/manage'); - exit; - } - } - else if ($step == 'resend') { - $twoFactorAuth->getAuthGateway()->resendEmailCode(); - - $twig->display('account.2fa.email_code.html.twig'); - } - else if ($step == 'confirm-activate') { - $account2faCode = $account_logged->getCustomField('2fa_email_code'); - $account2faCodeTimeout = $account_logged->getCustomField('2fa_email_code_timeout'); - - if (!empty($account2faCodeTimeout) && time() - (int)$account2faCodeTimeout < (24 * 60 * 60)) { - $postCode = $_POST['email-code'] ?? ''; - if (!empty($account2faCode)) { - if (!empty($postCode)) { - if ($postCode == $account2faCode) { - $twig->display('account.2fa.email-code.success.html.twig'); - } - } - else { - - } - } - else { - $errors[] = 'Your account dont have 2fa E-Mail code sent.'; - - } + if ($step == 'resend') { + if ($twoFactorAuth->hasRecentEmailCode(15 * 60)) { + $errors = ['Sorry, one email per 15 minutes']; } else { - $errors[] = 'E-Mail Code expired.'; + $twoFactorAuth->resendEmailCode(); } + + if (!empty($errors)) { + $twig->display('error_box.html.twig', ['errors' => $errors]); + } + + $twig->display('account.2fa.email-code.login.html.twig'); + } + else if ($step == 'activate') { + if (!$twoFactorAuth->hasRecentEmailCode(15 * 60)) { + $twoFactorAuth->resendEmailCode(); + } + + if (isset($_POST['save'])) { + if (!empty($code)) { + $twoFactorAuth->setAuthGateway(TwoFactorAuth::TYPE_EMAIL); + if ($twoFactorAuth->getAuthGateway()->verifyCode($code)) { + $serverName = configLua('serverName'); + + $twoFactorAuth->enable(TwoFactorAuth::TYPE_EMAIL); + $twoFactorAuth->deleteOldCodes(); + + $twig->display('success.html.twig', [ + 'title' => 'Email Code Authentication Activated', + 'description' => sprintf('You have successfully activated email code authentication for your account. This means an email code will be sent to the email address assigned to your account whenever you try to log in to the %s client or the %s website. In order to log in, you will need to enter the most recent email code you have received.', $serverName, $serverName) + ]); + + return; + } + else { + $errors[] = 'Invalid email code!'; + } + } + } + + if (!empty($errors)) { + $twig->display('error_box.html.twig', ['errors' => $errors]); + } + + $twig->display('account.2fa.email_code.html.twig', ['wrongCode' => count($errors) > 0]); + } + else if ($step == 'deactivate') { + if (!$twoFactorAuth->hasRecentEmailCode(15 * 60)) { + $twoFactorAuth->resendEmailCode(); + } + + if (isset($_POST['save'])) { + if (!empty($code)) { + if ($twoFactorAuth->getAuthGateway()->verifyCode($code)) { + + $twoFactorAuth->disable(); + $twoFactorAuth->deleteOldCodes(); + + $twig->display('success.html.twig', + [ + 'title' => 'Email Code Authentication Deactivated', + 'description' => 'You have successfully deactivated the Email Code Authentication for your account.' + ] + ); + + return; + } + else { + $errors[] = 'Invalid email code!'; + } + } + } + + if (!empty($errors)) { + $twig->display('error_box.html.twig', ['errors' => $errors]); + } + + $twig->display('account.2fa.email-code.deactivate.html.twig', ['wrongCode' => count($errors) > 0]); } } - -if (!empty($errors)) { - $twig->display('error_box.html.twig', ['errors' => $errors]); - $twig->display('account.back_button.html.twig', [ - 'new_line' => true - ]); -} diff --git a/system/pages/account/login.php b/system/pages/account/login.php index b4367c32..544656a2 100644 --- a/system/pages/account/login.php +++ b/system/pages/account/login.php @@ -58,8 +58,8 @@ if(!empty($login_account) && !empty($login_password)) setSession('remember_me', true); } - $twoFactorAuth = new TwoFactorAuth($account_logged); - if (!$twoFactorAuth->process()) { + $twoFactorAuth = TwoFactorAuth::getInstance($account_logged); + if (!$twoFactorAuth->process($_POST['email-code'] ?? '')) { return; } diff --git a/system/pages/account/manage.php b/system/pages/account/manage.php index 3776a732..9b4603f7 100644 --- a/system/pages/account/manage.php +++ b/system/pages/account/manage.php @@ -8,6 +8,9 @@ * @copyright 2019 MyAAC * @link https://my-aac.org */ + +use MyAAC\TwoFactorAuth\TwoFactorAuth; + defined('MYAAC') or die('Direct access not allowed!'); $title = 'Account Management'; @@ -111,6 +114,8 @@ $twig->display('account.management.html.twig', array( 'account_registered' => $account_registered, 'account_rlname' => $account_rlname, 'account_location' => $account_location, + 'twoFactorViews' => TwoFactorAuth::getInstance($account_logged)->getAccountManageViews(), + 'actions' => $actions, - 'players' => $account_players + 'players' => $account_players, )); diff --git a/system/src/TwoFactorAuth/Gateway/EmailAuthGateway.php b/system/src/TwoFactorAuth/Gateway/EmailAuthGateway.php index 0e2a4da7..90e7f311 100644 --- a/system/src/TwoFactorAuth/Gateway/EmailAuthGateway.php +++ b/system/src/TwoFactorAuth/Gateway/EmailAuthGateway.php @@ -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'); - } - } } diff --git a/system/src/TwoFactorAuth/TwoFactorAuth.php b/system/src/TwoFactorAuth/TwoFactorAuth.php index 8eafbcc5..f4de835a 100644 --- a/system/src/TwoFactorAuth/TwoFactorAuth.php +++ b/system/src/TwoFactorAuth/TwoFactorAuth.php @@ -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'); + } + } } diff --git a/system/templates/account.2fa.connect.html.twig b/system/templates/account.2fa.connect.html.twig new file mode 100644 index 00000000..bfe70dd8 --- /dev/null +++ b/system/templates/account.2fa.connect.html.twig @@ -0,0 +1,36 @@ + + +
+
+
+
+
+ + + + + + + + +
Connect your {{ config.lua.serverName }} account to an authenticator app! +
+
+ {{ csrf() }} + {% set button_name = 'Request' %} + {% include('buttons.base.html.twig') %} +
+
+
+

As a first step to connect an authenticator app to your account, click on "Request"! An email with a confirmation key will be sent to the email address assigned to your account.

+
+
+
+
+
+
+
+
+
+ + diff --git a/system/templates/account.2fa.email-code.deactivate.html.twig b/system/templates/account.2fa.email-code.deactivate.html.twig new file mode 100644 index 00000000..a455f105 --- /dev/null +++ b/system/templates/account.2fa.email-code.deactivate.html.twig @@ -0,0 +1,109 @@ +{% set title = 'Deactivate Email Code Authentication' %} +{% set content %} + + + + + + + + + + + + +
+
+ + + + + + +
To deactivate two-factor email code authentication for your account, enter the + received email code below. Note, however, that email code authentication + is an important security feature which helps to prevent any unauthorised access to your + Tibia account. +
+
+
+
+ + + + + + +
+
+
+ {{ csrf() }} + + {% set button_name = 'Resend Email Code' %} + {{ include('buttons.base.html.twig') }} +
+
+ An email code has already been sent to the email address assigned to your + account. + Please check your email account's spam/junk filter and make sure that your mailbox is + not + full.
In case you need a new email code, you can request one by clicking on "Resend + Email + Code". +
+
+
+
+ + + + + + +
To complete the deactivation of email code authentication, please enter the email + code you received at the email address assigned to your account. +
+
+ + {% if wrongCode %} +
+
 
+
Invalid email code!
+ {% endif %} +
+
+
+
+{% endset %} +{% include 'tables.headline.html.twig' %} + + + + + + + +
+
+ {{ csrf() }} + + + + + {% set button_name = 'Continue' %} + {% set button_color = 'green' %} + {{ include('buttons.submit.html.twig') }} +
+
+
+ {{ csrf() }} + {% set button_color = 'blue' %} + {{ include('buttons.back.html.twig') }} +
+
diff --git a/system/templates/account.2fa.email-code.login.html.twig b/system/templates/account.2fa.email-code.login.html.twig index 6990e6ba..e31f15d4 100644 --- a/system/templates/account.2fa.email-code.login.html.twig +++ b/system/templates/account.2fa.email-code.login.html.twig @@ -16,6 +16,7 @@ style="padding:0;margin:0;" > {{ csrf() }} + {% set button_name = 'Resend Email Code' %} {{ include('buttons.base.html.twig') }} @@ -40,11 +41,13 @@ Email code authentication is activated for your account.

Please enter the most recent email code you have received in order to log in.
-
Email Code:
-
- {{ csrf() }} - -
+
+ + {% if wrongCode %} +
+
 
+
Invalid email code!
+ {% endif %}
@@ -57,3 +60,26 @@ {% endset %} {% include 'tables.headline.html.twig' %} + + + + + + + +
+
+ {{ csrf() }} + + + {% set button_name = 'Continue' %} + {% set button_color = 'green' %} + {{ include('buttons.submit.html.twig') }} +
+
+
+ {{ csrf() }} + {% set button_color = 'blue' %} + {{ include('buttons.back.html.twig') }} +
+
diff --git a/system/templates/account.2fa.email-code.success.html.twig b/system/templates/account.2fa.email-code.success.html.twig deleted file mode 100644 index 9ed65b5f..00000000 --- a/system/templates/account.2fa.email-code.success.html.twig +++ /dev/null @@ -1,15 +0,0 @@ -{% set title = 'Email Code Authentication Activated' %} -{% set content %} - - - - - - -
You have successfully activated email code authentication for your account. This means an email - code will be sent to the email address assigned to your account whenever you try to log in to the - {{ config.lua.serverName }} client or the {{ config.lua.serverName }} website. In order to log in, you will need to enter the most recent email code you have received. -
-{% endset %} -{% include 'tables.headline.html.twig' %} -{{ include('account.back_button.html.twig') }} diff --git a/system/templates/account.2fa.email.activate.html.twig b/system/templates/account.2fa.email.activate.html.twig new file mode 100644 index 00000000..a0220aab --- /dev/null +++ b/system/templates/account.2fa.email.activate.html.twig @@ -0,0 +1,37 @@ + + +
+
+
+
+
+ + + + + + + + + +
Activate email code authentication for your account! +
+
+ {{ csrf() }} + {% set button_name = 'Request' %} + {% include('buttons.base.html.twig') %} +
+
+
+

As a first step to activate email code authentication for your account, click on "Request"! An email code will be sent to the email address assigned to your account. You will be asked to enter this email code on the next page within 24 hours.

+
+
+
+
+
+
+
+
+
+ + diff --git a/system/templates/account.2fa.email.activated.html.twig b/system/templates/account.2fa.email.activated.html.twig new file mode 100644 index 00000000..06e41fad --- /dev/null +++ b/system/templates/account.2fa.email.activated.html.twig @@ -0,0 +1,26 @@ + + +
+ + + + + + +
+
+
+ {{ csrf() }} + + {% set button_name = 'Deactivate' %} + {{ include('buttons.base.html.twig') }} +
+
+ Two-Factor Email Code Authentication Activated! +

To deactivate email code authentication, click on the "Deactivate" button.

+

You will have to confirm the deactivation by entering an email code which will be sent + to the email address assigned to your account.

+
+
+ + diff --git a/templates/tibiacom/account.2fa.email_code.html.twig b/system/templates/account.2fa.email_code.html.twig similarity index 78% rename from templates/tibiacom/account.2fa.email_code.html.twig rename to system/templates/account.2fa.email_code.html.twig index 17cf9132..f6ccb8da 100644 --- a/templates/tibiacom/account.2fa.email_code.html.twig +++ b/system/templates/account.2fa.email_code.html.twig @@ -31,9 +31,13 @@
-
- {{ set button_name = 'Resend Email Code' }} + + {{ csrf() }} + + + + {% set button_name = 'Resend Email Code' %} {% include('buttons.base.html.twig') %}
@@ -57,8 +61,13 @@ To complete the activation of email code authentication for your Tibia account, please enter the email code you received at the email address assigned to your account.
-
Email Code:
- +
Email Code:
+ + {% if wrongCode %} +
+
 
+
Invalid email code!
+ {% endif %}
@@ -70,18 +79,24 @@ {% endset %} +{% include 'tables.headline.html.twig' %} diff --git a/system/templates/account.2fa.main.html.twig b/system/templates/account.2fa.main.html.twig new file mode 100644 index 00000000..03dd4ead --- /dev/null +++ b/system/templates/account.2fa.main.html.twig @@ -0,0 +1,12 @@ +{% set title = 'Two-Factor Authentication' %} + +{% set content %} +
- + {{ csrf() }} + + + + {% set button_color = 'green' %} {{ include('buttons.submit.html.twig') }}
+ {{ csrf() }} {{ include('buttons.back.html.twig') }}
+ + {{ include(twoFactorViews[0]) }} + {{ include(twoFactorViews[1]) }} + +
+{% endset %} +{% include('tables.headline.html.twig') %} +
diff --git a/system/templates/account.2fa.protected.html.twig b/system/templates/account.2fa.protected.html.twig new file mode 100644 index 00000000..e0fa4f5b --- /dev/null +++ b/system/templates/account.2fa.protected.html.twig @@ -0,0 +1,18 @@ + + +
+ + + + + + +
+
+ Two-Factor Authenticator App +

Your account is currently protected by email code authentication. If you prefer to use a two-factor + authentication app, you have to "Deactivate" email code authentication first.

+
+
+ + diff --git a/system/templates/account.management.html.twig b/system/templates/account.management.html.twig index 03e2c2a6..40816173 100644 --- a/system/templates/account.management.html.twig +++ b/system/templates/account.management.html.twig @@ -147,6 +147,9 @@ {% include('buttons.base.html.twig') %}
+ + {{ include('account.2fa.main.html.twig') }} + {{ hook('HOOK_ACCOUNT_MANAGE_BEFORE_ACCOUNT_LOGS') }}

Account Logs

diff --git a/templates/tibiacom/account.2fa.main.html.twig b/templates/tibiacom/account.2fa.main.html.twig deleted file mode 100644 index 7da39e61..00000000 --- a/templates/tibiacom/account.2fa.main.html.twig +++ /dev/null @@ -1,81 +0,0 @@ -{% set title = 'Two-Factor Authentication' %} - -{% set content %} - - - - - - - - - -
-
-
-
-
-
- - - - - - - - -
Connect your {{ config.lua.serverName }} account to an authenticator app! -
-
- {{ csrf() }} - {% set button_name = 'Request' %} - {% include('buttons.base.html.twig') %} -
-
-
-

As a first step to connect an authenticator app to your account, click on "Request"! An email with a confirmation key will be sent to the email address assigned to your account.

-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- - - - - - - - -
Activate email code authentication for your account! -
-
- {{ csrf() }} - {% set button_name = 'Request' %} - {% include('buttons.base.html.twig') %} -
-
-
-

As a first step to activate email code authentication for your account, click on "Request"! An email code will be sent to the email address assigned to your account. You will be asked to enter this email code on the next page within 24 hours.

-
-
-
-
-
-
-
-
-
-
-{% endset %} -{% include('tables.headline.html.twig') %} diff --git a/templates/tibiacom/account.management.html.twig b/templates/tibiacom/account.management.html.twig index 649eba36..6a32ef31 100644 --- a/templates/tibiacom/account.management.html.twig +++ b/templates/tibiacom/account.management.html.twig @@ -290,6 +290,9 @@ {% endset %} {% include 'tables.headline.html.twig' %}
+ +{{ include('account.2fa.main.html.twig') }} + {{ hook('HOOK_ACCOUNT_MANAGE_BEFORE_ACCOUNT_LOGS') }}