From ec912bf4fe95bd8c4c29dd032783a10bbafefbe1 Mon Sep 17 00:00:00 2001 From: Leon Morival Date: Wed, 20 May 2026 23:17:25 +0200 Subject: [PATCH] feat: fix verify email --- app/Providers/AppServiceProvider.php | 14 ++++++++++++++ routes/api.php | 2 +- tests/Feature/EmailVerificationTest.php | 24 ++++++++++++++++++++++-- 3 files changed, 37 insertions(+), 3 deletions(-) diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index 0d47c31..f2e9a0b 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -42,6 +42,20 @@ class AppServiceProvider extends ServiceProvider 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( fn (User $notifiable, string $verificationUrl): VerifyAccount => (new VerifyAccount($notifiable, $verificationUrl)) ->to($notifiable->getEmailForVerification()) diff --git a/routes/api.php b/routes/api.php index 47374c0..da13e50 100644 --- a/routes/api.php +++ b/routes/api.php @@ -14,7 +14,7 @@ Route::prefix('auth')->group(function (): void { Route::post('register', [AuthController::class, 'register']); Route::post('login', [AuthController::class, 'login']); Route::get('email/verify/{id}/{hash}', [AuthController::class, 'verifyEmail']) - ->middleware(['signed', 'throttle:6,1']) + ->middleware(['signed:relative', 'throttle:6,1']) ->name('verification.verify'); Route::post('email/verification-notification', [AuthController::class, 'sendVerificationEmail']) ->middleware('throttle:6,1') diff --git a/tests/Feature/EmailVerificationTest.php b/tests/Feature/EmailVerificationTest.php index e7a77b6..fd9244e 100644 --- a/tests/Feature/EmailVerificationTest.php +++ b/tests/Feature/EmailVerificationTest.php @@ -41,12 +41,32 @@ it('uses the custom account verification mailable', function () { ->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 () { $user = User::factory()->unverified()->create(); $url = URL::temporarySignedRoute('verification.verify', now()->addMinutes(60), [ 'id' => $user->getKey(), 'hash' => sha1($user->email), - ]); + ], absolute: false); $this->getJson($url) ->assertOk() @@ -61,7 +81,7 @@ it('rejects signed verification links with an invalid hash', function () { $url = URL::temporarySignedRoute('verification.verify', now()->addMinutes(60), [ 'id' => $user->getKey(), 'hash' => sha1('wrong-address@example.com'), - ]); + ], absolute: false); $this->getJson($url)->assertForbidden();