From 9b37a1a7eb98a9c7ee88d565f01a4ff50d529425 Mon Sep 17 00:00:00 2001 From: Andras Bacsai <5845193+andrasbacsai@users.noreply.github.com> Date: Mon, 20 Apr 2026 12:08:46 +0200 Subject: [PATCH] refactor(auth): drop implicit email verification on invitation link login The invitation-link login path previously marked the account as email-verified as a side effect of authenticating, without the user ever proving control of the mailbox. Remove that branch so every account goes through the standard signed-URL verification flow. Co-Authored-By: Claude Opus 4.7 --- app/Http/Controllers/Controller.php | 4 -- .../LinkLoginEmailVerificationTest.php | 60 +++++++++++++++++++ 2 files changed, 60 insertions(+), 4 deletions(-) create mode 100644 tests/Feature/LinkLoginEmailVerificationTest.php diff --git a/app/Http/Controllers/Controller.php b/app/Http/Controllers/Controller.php index 17d14296b..a6b7f6440 100644 --- a/app/Http/Controllers/Controller.php +++ b/app/Http/Controllers/Controller.php @@ -94,10 +94,6 @@ public function link() } else { $team = $user->teams()->first(); } - if (is_null(data_get($user, 'email_verified_at'))) { - $user->email_verified_at = now(); - $user->save(); - } Auth::login($user); session(['currentTeam' => $team]); diff --git a/tests/Feature/LinkLoginEmailVerificationTest.php b/tests/Feature/LinkLoginEmailVerificationTest.php new file mode 100644 index 000000000..036584e1e --- /dev/null +++ b/tests/Feature/LinkLoginEmailVerificationTest.php @@ -0,0 +1,60 @@ +withoutMiddleware([DecideWhatToDoWithUser::class, CheckForcePasswordReset::class]); + Once::flush(); + if (! InstanceSettings::find(0)) { + $settings = new InstanceSettings; + $settings->id = 0; + $settings->saveQuietly(); + } +}); + +describe('invitation link login', function () { + test('does not auto-verify the email address', function () { + $team = Team::factory()->create(); + $password = 'test-password-123'; + $user = User::factory()->create([ + 'email' => 'invitee@example.com', + 'password' => Hash::make($password), + 'email_verified_at' => null, + ]); + $user->teams()->attach($team->id, ['role' => 'member']); + + $token = Crypt::encryptString("{$user->email}@@@{$password}"); + + $this->get(route('auth.link', ['token' => $token])); + + $user->refresh(); + expect($user->email_verified_at)->toBeNull(); + }); + + test('still logs the user in', function () { + $team = Team::factory()->create(); + $password = 'test-password-123'; + $user = User::factory()->create([ + 'email' => 'invitee2@example.com', + 'password' => Hash::make($password), + 'email_verified_at' => null, + ]); + $user->teams()->attach($team->id, ['role' => 'member']); + + $token = Crypt::encryptString("{$user->email}@@@{$password}"); + + $this->get(route('auth.link', ['token' => $token])); + + expect(auth()->id())->toBe($user->id); + }); +});