feat: resend email
Laravel CI-CD / Tests Unitaires (push) Failing after 22s Details
Laravel CI-CD / Deploy with Kamal (push) Has been skipped Details

# Conflicts:
#	app/Http/Controllers/AuthController.php
#	lang/en/api.php
#	lang/fr/api.php
#	tests/Feature/EmailVerificationTest.php
This commit is contained in:
Leon Morival 2026-05-20 21:03:35 +02:00
parent bcc067ec67
commit 54503caf45
6 changed files with 60 additions and 10 deletions

View File

@ -2,6 +2,7 @@
namespace App\Http\Controllers; namespace App\Http\Controllers;
use App\Http\Requests\EmailVerificationNotificationRequest;
use App\Http\Requests\LoginRequest; use App\Http\Requests\LoginRequest;
use App\Http\Requests\RegisterRequest; use App\Http\Requests\RegisterRequest;
use App\Http\Requests\UpdateUserRequest; use App\Http\Requests\UpdateUserRequest;
@ -107,9 +108,10 @@ class AuthController extends Controller
]); ]);
} }
public function sendVerificationEmail(Request $request): JsonResponse public function sendVerificationEmail(EmailVerificationNotificationRequest $request): JsonResponse
{ {
$user = $request->user(); $email = strtolower($request->validated('email'));
$user = User::where('email', $email)->first();
if ($user->hasVerifiedEmail()) { if ($user->hasVerifiedEmail()) {
return response()->json([ return response()->json([
@ -120,7 +122,7 @@ class AuthController extends Controller
$user->sendEmailVerificationNotification(); $user->sendEmailVerificationNotification();
return response()->json([ return response()->json([
'message' => __('api.auth.verification_sent'), 'message' => __('api.auth.verification_sent_if_unverified'),
]); ]);
} }

View File

@ -0,0 +1,25 @@
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class EmailVerificationNotificationRequest extends FormRequest
{
public function authorize(): bool
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
*/
public function rules(): array
{
return [
'email' => ['required', 'string', 'email'],
];
}
}

View File

@ -8,6 +8,7 @@ return [
'already_verified' => 'Account already verified.', 'already_verified' => 'Account already verified.',
'verified' => 'Account verified successfully.', 'verified' => 'Account verified successfully.',
'verification_sent' => 'Verification email sent.', 'verification_sent' => 'Verification email sent.',
'verification_sent_if_unverified' => 'If an unverified account exists for this address, a verification email has been sent.',
'logout' => 'Logged out successfully.', 'logout' => 'Logged out successfully.',
'unauthenticated' => 'Unauthenticated.', 'unauthenticated' => 'Unauthenticated.',
'deleted' => 'Account deleted successfully.', 'deleted' => 'Account deleted successfully.',

View File

@ -8,6 +8,7 @@ return [
'already_verified' => 'Compte déjà vérifié.', 'already_verified' => 'Compte déjà vérifié.',
'verified' => 'Compte vérifié avec succès.', 'verified' => 'Compte vérifié avec succès.',
'verification_sent' => 'Email de vérification envoyé.', 'verification_sent' => 'Email de vérification envoyé.',
'verification_sent_if_unverified' => 'Si un compte non vérifié existe avec cette adresse, un email de vérification vient dêtre envoyé.',
'logout' => 'Déconnecté avec succès.', 'logout' => 'Déconnecté avec succès.',
'unauthenticated' => 'Non authentifié.', 'unauthenticated' => 'Non authentifié.',
'deleted' => 'Compte supprimé avec succès.', 'deleted' => 'Compte supprimé avec succès.',

View File

@ -16,13 +16,13 @@ Route::prefix('auth')->group(function (): void {
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', 'throttle:6,1'])
->name('verification.verify'); ->name('verification.verify');
Route::post('email/verification-notification', [AuthController::class, 'sendVerificationEmail'])
->middleware('throttle:6,1')
->name('verification.send');
Route::middleware('auth:sanctum')->group(function (): void { Route::middleware('auth:sanctum')->group(function (): void {
Route::post('logout', [AuthController::class, 'logout']); Route::post('logout', [AuthController::class, 'logout']);
Route::get('me', [AuthController::class, 'me']); Route::get('me', [AuthController::class, 'me']);
Route::post('email/verification-notification', [AuthController::class, 'sendVerificationEmail'])
->middleware('throttle:6,1')
->name('verification.send');
Route::patch('me', [AuthController::class, 'update'])->middleware('verified'); Route::patch('me', [AuthController::class, 'update'])->middleware('verified');
Route::delete('me', [AuthController::class, 'destroy']); Route::delete('me', [AuthController::class, 'destroy']);
}); });

View File

@ -101,19 +101,40 @@ it('allows login for verified users', function () {
->assertCookie('token'); ->assertCookie('token');
}); });
it('resends a verification email for an authenticated unverified user', function () { it('resends a verification email for an unverified user without authentication', function () {
Notification::fake(); Notification::fake();
$user = User::factory()->unverified()->create(); $user = User::factory()->unverified()->create();
Sanctum::actingAs($user);
$this->postJson('/api/auth/email/verification-notification') $this->postJson('/api/auth/email/verification-notification', [
'email' => $user->email,
])
->assertOk() ->assertOk()
->assertJsonPath('message', __('api.auth.verification_sent')); ->assertJsonPath('message', __('api.auth.verification_sent_if_unverified'));
Notification::assertSentTo($user, VerifyEmail::class); Notification::assertSentTo($user, VerifyEmail::class);
}); });
it('does not reveal whether an email can receive a verification email', function () {
Notification::fake();
$verifiedUser = User::factory()->create();
$this->postJson('/api/auth/email/verification-notification', [
'email' => $verifiedUser->email,
])
->assertOk()
->assertJsonPath('message', __('api.auth.verification_sent_if_unverified'));
$this->postJson('/api/auth/email/verification-notification', [
'email' => 'missing@example.com',
])
->assertOk()
->assertJsonPath('message', __('api.auth.verification_sent_if_unverified'));
Notification::assertNothingSent();
});
it('blocks verified routes for unverified users', function () { it('blocks verified routes for unverified users', function () {
$user = User::factory()->unverified()->create(); $user = User::factory()->unverified()->create();
Sanctum::actingAs($user); Sanctum::actingAs($user);