feat: fix verify email
Laravel CI-CD / Tests Unitaires (push) Successful in 22s Details
Laravel CI-CD / Deploy with Kamal (push) Successful in 1m46s Details

This commit is contained in:
Leon Morival 2026-05-20 23:17:25 +02:00
parent 789eadb544
commit ec912bf4fe
3 changed files with 37 additions and 3 deletions

View File

@ -42,6 +42,20 @@ class AppServiceProvider extends ServiceProvider
Notification::extend('expo', fn ($app) => $app->make(ExpoPushChannel::class)); Notification::extend('expo', fn ($app) => $app->make(ExpoPushChannel::class));
VerifyEmail::createUrlUsing(function (User $notifiable): string {
$relativeUrl = URL::temporarySignedRoute(
'verification.verify',
now()->addMinutes((int) config('auth.verification.expire', 60)),
[
'id' => $notifiable->getKey(),
'hash' => sha1($notifiable->getEmailForVerification()),
],
absolute: false,
);
return url($relativeUrl);
});
VerifyEmail::toMailUsing( VerifyEmail::toMailUsing(
fn (User $notifiable, string $verificationUrl): VerifyAccount => (new VerifyAccount($notifiable, $verificationUrl)) fn (User $notifiable, string $verificationUrl): VerifyAccount => (new VerifyAccount($notifiable, $verificationUrl))
->to($notifiable->getEmailForVerification()) ->to($notifiable->getEmailForVerification())

View File

@ -14,7 +14,7 @@ Route::prefix('auth')->group(function (): void {
Route::post('register', [AuthController::class, 'register']); Route::post('register', [AuthController::class, 'register']);
Route::post('login', [AuthController::class, 'login']); Route::post('login', [AuthController::class, 'login']);
Route::get('email/verify/{id}/{hash}', [AuthController::class, 'verifyEmail']) Route::get('email/verify/{id}/{hash}', [AuthController::class, 'verifyEmail'])
->middleware(['signed', 'throttle:6,1']) ->middleware(['signed:relative', 'throttle:6,1'])
->name('verification.verify'); ->name('verification.verify');
Route::post('email/verification-notification', [AuthController::class, 'sendVerificationEmail']) Route::post('email/verification-notification', [AuthController::class, 'sendVerificationEmail'])
->middleware('throttle:6,1') ->middleware('throttle:6,1')

View File

@ -41,12 +41,32 @@ it('uses the custom account verification mailable', function () {
->assertSeeInHtml($user->name); ->assertSeeInHtml($user->name);
}); });
it('generates verification links with a relative signature', function () {
$user = User::factory()->unverified()->create();
$verificationUrl = (new VerifyEmail)->toMail($user)->verificationUrl;
expect($verificationUrl)->toContain('/api/auth/email/verify/')
->and($verificationUrl)->toContain('signature=');
$urlParts = parse_url($verificationUrl);
$requestUrl = ($urlParts['path'] ?? '').'?'.($urlParts['query'] ?? '');
$this
->withServerVariables(['HTTP_HOST' => 'different-host.test'])
->getJson($requestUrl)
->assertOk()
->assertJsonPath('message', __('api.auth.verified'));
expect($user->fresh()->hasVerifiedEmail())->toBeTrue();
});
it('verifies a user from a signed email link', function () { it('verifies a user from a signed email link', function () {
$user = User::factory()->unverified()->create(); $user = User::factory()->unverified()->create();
$url = URL::temporarySignedRoute('verification.verify', now()->addMinutes(60), [ $url = URL::temporarySignedRoute('verification.verify', now()->addMinutes(60), [
'id' => $user->getKey(), 'id' => $user->getKey(),
'hash' => sha1($user->email), 'hash' => sha1($user->email),
]); ], absolute: false);
$this->getJson($url) $this->getJson($url)
->assertOk() ->assertOk()
@ -61,7 +81,7 @@ it('rejects signed verification links with an invalid hash', function () {
$url = URL::temporarySignedRoute('verification.verify', now()->addMinutes(60), [ $url = URL::temporarySignedRoute('verification.verify', now()->addMinutes(60), [
'id' => $user->getKey(), 'id' => $user->getKey(),
'hash' => sha1('wrong-address@example.com'), 'hash' => sha1('wrong-address@example.com'),
]); ], absolute: false);
$this->getJson($url)->assertForbidden(); $this->getJson($url)->assertForbidden();