feat(email): implement email change request and verification process

- Added functionality for users to request an email change, including generating a verification code and setting an expiration time.
- Implemented methods in the User model to handle email change requests, code validation, and confirmation.
- Created a new job to update the user's email in Stripe after confirmation.
- Introduced rate limiting for email change requests and verification attempts to prevent abuse.
- Added a new notification for email change verification.
- Updated the profile component to manage email change requests and verification UI.
This commit is contained in:
Andras Bacsai
2025-08-18 14:54:08 +02:00
parent 5cfe6464aa
commit ee502b9f76
12 changed files with 568 additions and 4 deletions

View File

@@ -32,7 +32,8 @@
wire:dirty.class="dark:focus:ring-warning dark:ring-warning" wire:loading.attr="disabled"
type="{{ $type }}" @readonly($readonly) @disabled($disabled) id="{{ $id }}"
name="{{ $name }}" placeholder="{{ $attributes->get('placeholder') }}"
aria-placeholder="{{ $attributes->get('placeholder') }}" @if ($autofocus) x-ref="autofocusInput" @endif>
aria-placeholder="{{ $attributes->get('placeholder') }}"
@if ($autofocus) x-ref="autofocusInput" @endif>
</div>
@else
@@ -45,7 +46,8 @@
max="{{ $attributes->get('max') }}" minlength="{{ $attributes->get('minlength') }}"
maxlength="{{ $attributes->get('maxlength') }}"
@if ($id !== 'null') id={{ $id }} @endif name="{{ $name }}"
placeholder="{{ $attributes->get('placeholder') }}" @if ($autofocus) x-ref="autofocusInput" @endif>
placeholder="{{ $attributes->get('placeholder') }}"
@if ($autofocus) x-ref="autofocusInput" @endif>
@endif
@if (!$label && $helper)
<x-helper :helper="$helper" />

View File

@@ -0,0 +1,11 @@
<x-emails.layout>
You have requested to change your email address to: {{ $newEmail }}
Please use the following verification code to confirm this change:
Verification Code: {{ $verificationCode }}
This code is valid for {{ $expiryMinutes }} minutes.
If you did not request this change, please ignore this email and your email address will remain unchanged.
</x-emails.layout>

View File

@@ -9,11 +9,52 @@
<h2>General</h2>
<x-forms.button type="submit" label="Save">Save</x-forms.button>
</div>
<div class="flex flex-col gap-2 lg:flex-row">
<div class="flex flex-col gap-2 lg:flex-row items-end">
<x-forms.input id="name" label="Name" required />
<x-forms.input id="email" label="Email" readonly />
@if (!$show_email_change && !$show_verification)
<x-forms.button wire:click="showEmailChangeForm" type="button">Change Email</x-forms.button>
@else
<x-forms.button wire:click="showEmailChangeForm" type="button" disabled>Change Email</x-forms.button>
@endif
</div>
</form>
<div class="flex flex-col pt-4">
@if ($show_email_change)
<form wire:submit='requestEmailChange'>
<div class="flex gap-2 items-end">
<x-forms.input id="new_email" label="New Email Address" required type="email" />
<x-forms.button type="submit">Send Verification Code</x-forms.button>
<x-forms.button wire:click="$set('show_email_change', false)" type="button"
isError>Cancel</x-forms.button>
</div>
<div class="text-xs font-bold dark:text-warning pt-2">A verification code will be sent to your
new email
address.</div>
</form>
@endif
@if ($show_verification)
<form wire:submit='verifyEmailChange'>
<div class="flex gap-2 items-end">
<x-forms.input id="email_verification_code" label="Verification Code (6 digits)" required
maxlength="6" />
<x-forms.button type="submit">Verify & Update Email</x-forms.button>
<x-forms.button wire:click="resendVerificationCode" type="button" isWarning>Resend
Code</x-forms.button>
<x-forms.button wire:click="cancelEmailChange" type="button" isError>Cancel</x-forms.button>
</div>
<div class="text-xs font-bold dark:text-warning pt-2">
Verification code sent to {{ $new_email ?? auth()->user()->pending_email }}.
The code is valid for {{ config('constants.email_change.verification_code_expiry_minutes', 10) }}
minutes.
</div>
</form>
@endif
</div>
<form wire:submit='resetPassword' class="flex flex-col pt-4">
<div class="flex items-center gap-2 pb-2">
<h2>Change Password</h2>