Skip to content

Commit b4a9aba

Browse files
authored
[3.x] Add email verification route (#247)
1 parent 023c332 commit b4a9aba

File tree

5 files changed

+93
-1
lines changed

5 files changed

+93
-1
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Cachet\Http\Controllers\Auth;
6+
7+
use Illuminate\Foundation\Auth\EmailVerificationRequest;
8+
use Illuminate\Http\RedirectResponse;
9+
10+
final class VerifyEmailController
11+
{
12+
/**
13+
* Mark the authenticated user's email address as verified.
14+
*/
15+
public function __invoke(EmailVerificationRequest $request): RedirectResponse
16+
{
17+
$request->fulfill();
18+
19+
return redirect()->intended(route('cachet.status-page', absolute: false).'?verified=1');
20+
}
21+
}

src/Models/User.php

+2-1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
use Cachet\Concerns\CachetUser;
66
use Cachet\Database\Factories\UserFactory;
7+
use Illuminate\Auth\MustVerifyEmail as MustVerifyEmailTrait;
78
use Illuminate\Contracts\Auth\MustVerifyEmail;
89
use Illuminate\Contracts\Translation\HasLocalePreference;
910
use Illuminate\Database\Eloquent\Factories\Factory;
@@ -23,7 +24,7 @@
2324
class User extends Authenticatable implements CachetUser, HasLocalePreference, MustVerifyEmail
2425
{
2526
/** @use HasFactory<\Cachet\Database\Factories\UserFactory> */
26-
use HasApiTokens, HasFactory, Notifiable;
27+
use HasApiTokens, HasFactory, Notifiable, MustVerifyEmailTrait;
2728

2829
/**
2930
* The attributes that are mass assignable.

src/PendingRouteRegistration.php

+15
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
namespace Cachet;
44

5+
use Cachet\Http\Controllers\Auth\EmailVerificationPromptController;
6+
use Cachet\Http\Controllers\Auth\VerifyEmailController;
57
use Cachet\Http\Controllers\HealthController;
68
use Cachet\Http\Controllers\RssController;
79
use Cachet\Http\Controllers\Setup\SetupController;
@@ -40,9 +42,22 @@ public function register(): void
4042
$router->get('/health', HealthController::class)->name('health');
4143

4244
$router->get('/rss', RssController::class)->name('rss');
45+
4346
});
47+
48+
$this->registerEmailVerificationRoutes();
49+
}
50+
51+
private function registerEmailVerificationRoutes(): void
52+
{
53+
Route::middleware('auth')->group(function () {
54+
Route::get('verify-email/{id}/{hash}', VerifyEmailController::class)
55+
->middleware(['signed', 'throttle:6,1'])
56+
->name('verification.verify');
57+
});
4458
}
4559

60+
4661
/**
4762
* Handle the object's destruction.
4863
*
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Cachet\Tests\Feature\Auth;
6+
7+
use Illuminate\Auth\Events\Verified;
8+
use Illuminate\Support\Facades\Event;
9+
use Illuminate\Support\Facades\URL;
10+
use Workbench\App\User;
11+
12+
use function Pest\Laravel\actingAs;
13+
use function PHPUnit\Framework\assertFalse;
14+
use function PHPUnit\Framework\assertTrue;
15+
16+
test('email can be verified', function () {
17+
$user = User::factory()->unverified()->create();
18+
19+
Event::fake();
20+
21+
$verificationUrl = URL::temporarySignedRoute(
22+
'verification.verify',
23+
now()->addMinutes(60),
24+
['id' => $user->id, 'hash' => sha1($user->email)]
25+
);
26+
27+
$response = actingAs($user)->get($verificationUrl);
28+
29+
Event::assertDispatched(Verified::class);
30+
assertTrue($user->fresh()->hasVerifiedEmail());
31+
$response->assertRedirect(route('cachet.status-page', absolute: false).'?verified=1');
32+
});
33+
34+
test('email is not verified with invalid hash', function () {
35+
$user = User::factory()->unverified()->create();
36+
37+
$verificationUrl = URL::temporarySignedRoute(
38+
'verification.verify',
39+
now()->addMinutes(60),
40+
['id' => $user->id, 'hash' => sha1('wrong-email')]
41+
);
42+
43+
actingAs($user)->get($verificationUrl);
44+
assertFalse($user->fresh()->hasVerifiedEmail());
45+
});

workbench/database/factories/UserFactory.php

+10
Original file line numberDiff line numberDiff line change
@@ -44,4 +44,14 @@ public function active()
4444
];
4545
});
4646
}
47+
48+
/**
49+
* Indicate that the model's email address should be unverified.
50+
*/
51+
public function unverified(): static
52+
{
53+
return $this->state(fn (array $attributes) => [
54+
'email_verified_at' => null,
55+
]);
56+
}
4757
}

0 commit comments

Comments
 (0)