From 950bb290235dc314b32594593b430bf449a8abb9 Mon Sep 17 00:00:00 2001 From: Maurizio Date: Wed, 21 Apr 2021 14:10:56 +0200 Subject: [PATCH] feat: throttle verification mail resending in UI (#116) --- resources/lang/en/actions.php | 47 ++++++++++--------- resources/lang/en/auth.php | 3 +- resources/lang/en/messages.php | 2 +- resources/views/auth/verify-email.blade.php | 21 +-------- .../components/auth-verify-email.blade.php | 28 +++++++++++ src/Components/VerifyEmail.php | 42 +++++++++++++++++ src/FortifyServiceProvider.php | 2 + tests/Components/VerifyEmailTest.php | 27 +++++++++++ 8 files changed, 128 insertions(+), 44 deletions(-) create mode 100644 resources/views/components/auth-verify-email.blade.php create mode 100644 src/Components/VerifyEmail.php create mode 100644 tests/Components/VerifyEmailTest.php diff --git a/resources/lang/en/actions.php b/resources/lang/en/actions.php index 1bc4e013..efd15fd5 100644 --- a/resources/lang/en/actions.php +++ b/resources/lang/en/actions.php @@ -3,27 +3,28 @@ declare(strict_types=1); return [ - 'disable' => 'Disable', - 'enable' => 'Enable', - 'export_personal_data' => 'Export Personal Data', - 'understand' => 'I Understand', - 'update' => 'Update', - 'select_timezone' => 'Select Timezone', - 'sign_in' => 'Sign In', - 'sign_up' => 'Sign Up', - 'reset_password' => 'Reset Password', - 'enter_recovery_code' => 'Enter recovery code', - 'enter_2fa_code' => 'Enter 2FA code', - 'verify' => 'Verify', - 'delete_account' => 'Delete Account', - 'nevermind' => 'Nevermind', - 'done' => 'Done', - 'confirm_logout' => 'Logout Other Browser Sessions', - 'cancel' => 'Cancel', - 'delete' => 'Delete', - 'send' => 'Send', - 'skip' => 'Skip', - 'download' => 'Download', - 'recovery_codes' => 'Recovery Codes', - 'confirm' => 'Confirm', + 'disable' => 'Disable', + 'enable' => 'Enable', + 'export_personal_data' => 'Export Personal Data', + 'understand' => 'I Understand', + 'update' => 'Update', + 'select_timezone' => 'Select Timezone', + 'sign_in' => 'Sign In', + 'sign_up' => 'Sign Up', + 'reset_password' => 'Reset Password', + 'enter_recovery_code' => 'Enter recovery code', + 'enter_2fa_code' => 'Enter 2FA code', + 'verify' => 'Verify', + 'delete_account' => 'Delete Account', + 'nevermind' => 'Nevermind', + 'done' => 'Done', + 'confirm_logout' => 'Logout Other Browser Sessions', + 'cancel' => 'Cancel', + 'delete' => 'Delete', + 'send' => 'Send', + 'skip' => 'Skip', + 'download' => 'Download', + 'recovery_codes' => 'Recovery Codes', + 'confirm' => 'Confirm', + 'resend_email_verification' => 'click here to request another', ]; diff --git a/resources/lang/en/auth.php b/resources/lang/en/auth.php index 41726194..0db24539 100644 --- a/resources/lang/en/auth.php +++ b/resources/lang/en/auth.php @@ -39,6 +39,7 @@ 'verify' => [ 'page_header' => 'Verify Your Email Address', 'link_description' => 'A verification link has been sent to your email address.', - 'resend_verification' => 'Before proceeding, please check your email for a verification link. If you did not receive the email, .', + 'line_1' => 'Before proceeding, please check your email for a verification link.', + 'line_2' => 'If you did not receive the email,', ], ]; diff --git a/resources/lang/en/messages.php b/resources/lang/en/messages.php index 36a354f3..a9e491c1 100644 --- a/resources/lang/en/messages.php +++ b/resources/lang/en/messages.php @@ -5,9 +5,9 @@ return [ 'invalid_2fa_authentication_code' => 'The provided two factor authentication code was invalid', 'export_limit_reached' => 'You can export your personal data once every 15 minutes.', - 'invalid_2fa_authentication_code' => 'The provided two factor authentication code was invalid', 'subscription' => [ 'duplicate' => "You're already subscribed.", 'pending' => 'You should shortly receive an email to confirm your subscription. You may need to check your spam folder if an email doesn’t arrive within a few minutes.', ], + 'resend_email_verification_limit' => 'Please wait 5 minutes before requesting another e-mail. Ensure to check your spam folder.', ]; diff --git a/resources/views/auth/verify-email.blade.php b/resources/views/auth/verify-email.blade.php index ba22b109..77b548f1 100644 --- a/resources/views/auth/verify-email.blade.php +++ b/resources/views/auth/verify-email.blade.php @@ -14,23 +14,6 @@ @endsection @section('content') -
-
-
-

@lang('fortify::auth.verify.page_header')

- -

@lang('fortify::auth.verify.link_description')

-
- - - -
- @csrf - -

- @lang('fortify::auth.verify.resend_verification') -

-
-
-
+ @endsection + diff --git a/resources/views/components/auth-verify-email.blade.php b/resources/views/components/auth-verify-email.blade.php new file mode 100644 index 00000000..5977b294 --- /dev/null +++ b/resources/views/components/auth-verify-email.blade.php @@ -0,0 +1,28 @@ +
+
+
+

@lang('fortify::auth.verify.page_header')

+ +

@lang('fortify::auth.verify.link_description')

+
+ + + +
+

+ @lang('fortify::auth.verify.line_1') + @lang('fortify::auth.verify.line_2') + + @if($this->rateLimitReached()) + + @lang('fortify::actions.resend_email_verification') + + @else + + @endif +

+
+
+
diff --git a/src/Components/VerifyEmail.php b/src/Components/VerifyEmail.php new file mode 100644 index 00000000..7cf78ee4 --- /dev/null +++ b/src/Components/VerifyEmail.php @@ -0,0 +1,42 @@ +rateLimit(self::MAX_ATTEMPTS, self::DECAY_SECONDS); + } catch (TooManyRequestsException $e) { + return; + } + + (new EmailVerificationNotificationController())->store(request()); + } + + public function rateLimitReached(): bool + { + return RateLimiter::tooManyAttempts($this->getRateLimitKey('resend'), self::MAX_ATTEMPTS); + } +} diff --git a/src/FortifyServiceProvider.php b/src/FortifyServiceProvider.php index 673f953c..eeacaebf 100644 --- a/src/FortifyServiceProvider.php +++ b/src/FortifyServiceProvider.php @@ -20,6 +20,7 @@ use ARKEcosystem\Fortify\Components\UpdateProfileInformationForm; use ARKEcosystem\Fortify\Components\UpdateProfilePhotoForm; use ARKEcosystem\Fortify\Components\UpdateTimezoneForm; +use ARKEcosystem\Fortify\Components\VerifyEmail; use ARKEcosystem\Fortify\Http\Responses\FailedPasswordResetLinkRequestResponse as FortifyFailedPasswordResetLinkRequestResponse; use ARKEcosystem\Fortify\Http\Responses\SuccessfulPasswordResetLinkRequestResponse as FortifySuccessfulPasswordResetLinkRequestResponse; use ARKEcosystem\Fortify\Responses\FailedTwoFactorLoginResponse; @@ -130,6 +131,7 @@ public function registerLivewireComponents(): void Livewire::component('auth.register-form', RegisterForm::class); Livewire::component('auth.reset-password-form', ResetPasswordForm::class); Livewire::component('newsletter.footer-subscription-form', FooterEmailSubscriptionForm::class); + Livewire::component('auth.verify-email', VerifyEmail::class); } /** diff --git a/tests/Components/VerifyEmailTest.php b/tests/Components/VerifyEmailTest.php new file mode 100644 index 00000000..1cbf6f48 --- /dev/null +++ b/tests/Components/VerifyEmailTest.php @@ -0,0 +1,27 @@ +test(VerifyEmail::class) + ->call('resend') + ->assertDontSee(trans('fortify::messages.resend_email_verification_limit')); +}); + +it('can resend a verification email once every 5 minutes', function (): void { + $component = Livewire::actingAs(createUserModel()) + ->test(VerifyEmail::class) + ->call('resend') + ->call('resend') + ->assertSee(trans('fortify::messages.resend_email_verification_limit')); + + $this->travel(6)->minutes(); + + $component->call('$refresh')->assertDontSee(trans('fortify::messages.resend_email_verification_limit')); +});