From 59a6c9a946ac6dc8e24d56bd90eb2712a3350961 Mon Sep 17 00:00:00 2001 From: Brian Hanson Date: Fri, 12 Jul 2024 12:44:20 -0500 Subject: [PATCH 1/4] Core changes to make front-end 2FA possible --- src/auth/methods/RecoveryCodes.php | 3 ++- src/auth/methods/TOTP.php | 2 +- src/controllers/AuthController.php | 2 -- .../_components/auth/methods/RecoveryCodes/form.twig | 7 +++++++ src/templates/_components/auth/methods/TOTP/form.twig | 7 +++++++ 5 files changed, 17 insertions(+), 4 deletions(-) diff --git a/src/auth/methods/RecoveryCodes.php b/src/auth/methods/RecoveryCodes.php index ecdff8b4353..7caa21e6256 100644 --- a/src/auth/methods/RecoveryCodes.php +++ b/src/auth/methods/RecoveryCodes.php @@ -13,6 +13,7 @@ use craft\helpers\Json; use craft\records\RecoveryCodes as RecoveryCodesRecord; use craft\web\assets\recoverycodes\RecoveryCodesAsset; +use craft\web\View; use DateTime; use PragmaRX\Recovery\Recovery; use yii\base\InvalidArgumentException; @@ -72,7 +73,7 @@ public function getAuthFormHtml(): string { $view = Craft::$app->getView(); $view->registerAssetBundle(RecoveryCodesAsset::class); - return $view->renderTemplate('_components/auth/methods/RecoveryCodes/form.twig'); + return $view->renderTemplate('_components/auth/methods/RecoveryCodes/form.twig', [], View::TEMPLATE_MODE_CP); } /** diff --git a/src/auth/methods/TOTP.php b/src/auth/methods/TOTP.php index 015d6553291..76739086300 100644 --- a/src/auth/methods/TOTP.php +++ b/src/auth/methods/TOTP.php @@ -107,7 +107,7 @@ public function getAuthFormHtml(): string { $view = Craft::$app->getView(); $view->registerAssetBundle(TotpAsset::class); - return $view->renderTemplate('_components/auth/methods/TOTP/form.twig'); + return $view->renderTemplate('_components/auth/methods/TOTP/form.twig', [], View::TEMPLATE_MODE_CP); } /** diff --git a/src/controllers/AuthController.php b/src/controllers/AuthController.php index 35703d34147..e839befa265 100644 --- a/src/controllers/AuthController.php +++ b/src/controllers/AuthController.php @@ -44,8 +44,6 @@ public function beforeAction($action): bool return false; } - $this->requireCpRequest(); - ; return true; } diff --git a/src/templates/_components/auth/methods/RecoveryCodes/form.twig b/src/templates/_components/auth/methods/RecoveryCodes/form.twig index d6a257da86f..74ab3607005 100644 --- a/src/templates/_components/auth/methods/RecoveryCodes/form.twig +++ b/src/templates/_components/auth/methods/RecoveryCodes/form.twig @@ -3,6 +3,12 @@ {% set formId = formId ?? "recovery-codes-form-#{random()}" %}
+ {{ actionInput('auth/verify-totp') }} + + {% if craft.app.config.general.enableCsrfProtection %} + {{ csrfInput() }} + {% endif %} + {% embed '_includes/forms/field.twig' with { fieldClass: 'first', label: 'Recovery Code'|t('app'), @@ -12,6 +18,7 @@
{{ forms.text({ class: 'code auth-recovery-code', + name: 'code', maxlength: 13, }) }} {{ forms.submitButton({ diff --git a/src/templates/_components/auth/methods/TOTP/form.twig b/src/templates/_components/auth/methods/TOTP/form.twig index d52d9286911..9c590b90d8b 100644 --- a/src/templates/_components/auth/methods/TOTP/form.twig +++ b/src/templates/_components/auth/methods/TOTP/form.twig @@ -3,6 +3,12 @@ {% set formId = formId ?? "totp-form-#{random()}" %} + {{ actionInput('auth/verify-totp') }} + + {% if craft.app.config.general.enableCsrfProtection %} + {{ csrfInput() }} + {% endif %} + {% embed '_includes/forms/field.twig' with { fieldClass: 'first', label: 'Verification Code'|t('app'), @@ -13,6 +19,7 @@
{{ forms.text({ class: 'code auth-totp-code', + name: 'code', id: 'verification-code', maxlength: 6, }) }} From e64715eddc072b095fad8fc00f230e9861a8ea95 Mon Sep 17 00:00:00 2001 From: Brian Hanson Date: Thu, 18 Jul 2024 14:10:41 -0500 Subject: [PATCH 2/4] Add `getSetupData` method to `AuthMethodInterface` --- src/auth/methods/AuthMethodInterface.php | 7 +++++++ src/auth/methods/BaseAuthMethod.php | 5 +++++ src/auth/methods/RecoveryCodes.php | 2 +- src/auth/methods/TOTP.php | 19 +++++++++++++------ 4 files changed, 26 insertions(+), 7 deletions(-) diff --git a/src/auth/methods/AuthMethodInterface.php b/src/auth/methods/AuthMethodInterface.php index 3083fe1ab4c..d64dbf0507f 100644 --- a/src/auth/methods/AuthMethodInterface.php +++ b/src/auth/methods/AuthMethodInterface.php @@ -66,6 +66,13 @@ public function isActive(): bool; */ public function getSetupHtml(string $containerId): string; + /** + * Returns the raw data provided to the template rendered via [[getSetupHtml()]] + * + * @return array + */ + public function getSetupData(): array; + /** * Returns the HTML for the authentication method’s authentication form. * diff --git a/src/auth/methods/BaseAuthMethod.php b/src/auth/methods/BaseAuthMethod.php index 2fefad88130..19044e3f998 100644 --- a/src/auth/methods/BaseAuthMethod.php +++ b/src/auth/methods/BaseAuthMethod.php @@ -23,6 +23,11 @@ abstract class BaseAuthMethod extends Component implements AuthMethodInterface */ protected User $user; + public function getSetupData(): array + { + return []; + } + /** * @inheritdoc */ diff --git a/src/auth/methods/RecoveryCodes.php b/src/auth/methods/RecoveryCodes.php index 7caa21e6256..6440b89fa6b 100644 --- a/src/auth/methods/RecoveryCodes.php +++ b/src/auth/methods/RecoveryCodes.php @@ -63,7 +63,7 @@ public function getSetupHtml(string $containerId): string new Craft.RecoveryCodesSetup($containerId); JS, [$containerId]); - return $view->renderTemplate('_components/auth/methods/RecoveryCodes/setup.twig'); + return $view->renderTemplate('_components/auth/methods/RecoveryCodes/setup.twig', $this->getSetupData(), View::TEMPLATE_MODE_CP); } /** diff --git a/src/auth/methods/TOTP.php b/src/auth/methods/TOTP.php index 76739086300..e78a638d718 100644 --- a/src/auth/methods/TOTP.php +++ b/src/auth/methods/TOTP.php @@ -72,12 +72,20 @@ public function isActive(): bool return self::secretFromDb($this->user->id) !== null; } + public function getSetupData(): array + { + $secret = $this->secret(); + return [ + 'secret' => trim($secret), + 'qrCode' => $this->generateQrCode($secret), + ]; + } + /** * @inheritdoc */ public function getSetupHtml(string $containerId): string { - $secret = $this->secret(); $totpFormId = sprintf('totp-form-%s', mt_rand()); $view = Craft::$app->getView(); @@ -92,12 +100,11 @@ public function getSetupHtml(string $containerId): string $containerId, ]); - return $view->renderTemplate('_components/auth/methods/TOTP/setup.twig', [ - 'secret' => $secret, - 'user' => $this->user, - 'qrCode' => $this->generateQrCode($secret), + $templateData = array_merge($this->getSetupData(), [ 'totpFormId' => $totpFormId, - ], View::TEMPLATE_MODE_CP); + ]); + + return $view->renderTemplate('_components/auth/methods/TOTP/setup.twig', $templateData, View::TEMPLATE_MODE_CP); } /** From 9ef69adb479039585165c62962bc39d408194d11 Mon Sep 17 00:00:00 2001 From: Brian Hanson Date: Tue, 23 Jul 2024 10:15:28 -0500 Subject: [PATCH 3/4] Fix recovery code action --- .../_components/auth/methods/RecoveryCodes/form.twig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/templates/_components/auth/methods/RecoveryCodes/form.twig b/src/templates/_components/auth/methods/RecoveryCodes/form.twig index 74ab3607005..dfdd1f40b41 100644 --- a/src/templates/_components/auth/methods/RecoveryCodes/form.twig +++ b/src/templates/_components/auth/methods/RecoveryCodes/form.twig @@ -2,8 +2,8 @@ {% set formId = formId ?? "recovery-codes-form-#{random()}" %} - - {{ actionInput('auth/verify-totp') }} + + {{ actionInput('auth/verify-recovery-code') }} {% if craft.app.config.general.enableCsrfProtection %} {{ csrfInput() }} From 27ff6b9adbcdb9d42690f9c02e2774b542570a93 Mon Sep 17 00:00:00 2001 From: Brian Hanson Date: Tue, 23 Jul 2024 10:15:38 -0500 Subject: [PATCH 4/4] Post request --- src/templates/_components/auth/methods/TOTP/form.twig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/templates/_components/auth/methods/TOTP/form.twig b/src/templates/_components/auth/methods/TOTP/form.twig index 9c590b90d8b..aa69d54c334 100644 --- a/src/templates/_components/auth/methods/TOTP/form.twig +++ b/src/templates/_components/auth/methods/TOTP/form.twig @@ -2,7 +2,7 @@ {% set formId = formId ?? "totp-form-#{random()}" %} - + {{ actionInput('auth/verify-totp') }} {% if craft.app.config.general.enableCsrfProtection %}