Skip to content

Commit 90a75a2

Browse files
committed
feat: (LAR-107) Modification du système d'authentification fortify avec Breeze
1 parent a704d2e commit 90a75a2

File tree

10 files changed

+276
-17117
lines changed

10 files changed

+276
-17117
lines changed

composer.lock

Lines changed: 0 additions & 17096 deletions
This file was deleted.
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
@props(['status'])
2+
3+
@if ($status)
4+
<div {{ $attributes->merge(['class' => 'font-medium text-sm text-green-600 dark:text-green-400']) }}>
5+
{{ $status }}
6+
</div>
7+
@endif

resources/views/livewire/pages/auth/forgot-password.blade.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,14 +37,16 @@ public function sendPasswordResetLink(): void
3737
<div>
3838
<x-status-message class="mb-5" />
3939

40+
<x-validation-errors class="mb-5"/>
41+
4042
<h2 class="text-center font-heading text-3xl font-extrabold text-gray-900 dark:text-white sm:text-left">
4143
{{ __('pages/auth.forgot.page_title') }}
4244
</h2>
4345
<div class="mt-4 text-sm text-gray-500 dark:text-gray-400">
4446
{{ __('pages/auth.forgot.description') }}
4547
</div>
4648
</div>
47-
49+
4850
<form class="mt-8" wire:submit="sendPasswordResetLink">
4951

5052
<div class="block">

resources/views/livewire/pages/auth/login.blade.php

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,15 @@ public function login(): void
2727
<x-container class="flex min-h-full items-center justify-center py-16 sm:pt-24">
2828
<div class="w-full max-w-md space-y-8">
2929
<div>
30-
<x-validation-errors />
30+
<x-validation-errors class="mb-5" />
31+
32+
<x-status-message class="mb-5" />
3133

3234
<h2 class="text-center font-heading text-3xl font-extrabold text-gray-900 dark:text-white">
3335
{{ __('pages/auth.login.title') }}
3436
</h2>
3537
</div>
38+
3639
<form class="space-y-6" wire:submit="login">
3740
<div class="space-y-4">
3841
<x-filament::input.wrapper>

resources/views/livewire/pages/auth/register.blade.php

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,11 @@ public function register(): void
2525
->mixedCase()],
2626
]);
2727
28-
$validated['password'] = Hash::make($validated['password']);
28+
$user = User::create($validated);
2929
30-
event(new Registered(User::create($validated)));
30+
$user->assignRole('user');
31+
32+
event(new Registered($user));
3133
3234
session()->flash('status', __('pages/auth.register.email_verification_status'));
3335
}
Lines changed: 86 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,54 +1,125 @@
1-
<x-app-layout :title="__('pages/auth.reset.page_title')">
1+
<?php
2+
3+
use Illuminate\Auth\Events\PasswordReset;
4+
use Illuminate\Support\Facades\Hash;
5+
use Illuminate\Support\Facades\Session;
6+
use Illuminate\Support\Str;
7+
use Illuminate\Validation\Rules;
8+
use Livewire\Attributes\Layout;
9+
use Livewire\Attributes\Locked;
10+
use Livewire\Volt\Component;
11+
use Illuminate\Support\Facades\Password;
12+
use Illuminate\Validation\Rules\Password as PasswordRules;
13+
14+
new class extends Component
15+
{
16+
#[Locked]
17+
public string $token = '';
18+
public string $email = '';
19+
public string $password = '';
20+
public string $password_confirmation = '';
21+
22+
/**
23+
* Mount the component.
24+
*/
25+
public function mount(string $token): void
26+
{
27+
$this->token = $token;
28+
29+
$this->email = request()->string('email');
30+
}
31+
32+
/**
33+
* Reset the password for the given user.
34+
*/
35+
public function resetPassword(): void
36+
{
37+
$this->validate([
38+
'token' => ['required'],
39+
'email' => ['required', 'string', 'email'],
40+
'password' => ['required', 'string', 'confirmed', PasswordRules::min(8)
41+
->uncompromised()
42+
->numbers()
43+
->mixedCase()],
44+
]);
45+
46+
$status = Password::reset(
47+
$this->only('email', 'password', 'password_confirmation', 'token'),
48+
function ($user) {
49+
$user->forceFill([
50+
'password' => Hash::make($this->password),
51+
'remember_token' => Str::random(60),
52+
])->save();
53+
54+
event(new PasswordReset($user));
55+
}
56+
);
57+
58+
if ($status != Password::PASSWORD_RESET) {
59+
$this->addError('email', __($status));
60+
61+
return;
62+
}
63+
64+
Session::flash('status', __($status));
65+
66+
$this->redirectRoute('login', navigate: true);
67+
}
68+
}; ?>
69+
70+
<div>
271
<div class="flex min-h-full items-center justify-center py-16 sm:py-24">
372
<div class="w-full max-w-md">
73+
74+
<x-status-message class="mb-5" />
75+
76+
<x-validation-errors class="mb-5"/>
77+
478
<h2 class="text-center font-heading text-3xl font-bold text-gray-900 sm:text-left">
579
{{ __('pages/auth.reset.page_title') }}
680
</h2>
781

8-
<form action="{{ route('password.update') }}" method="POST" class="mt-8 space-y-6">
9-
@csrf
10-
<input type="hidden" name="token" value="{{ $request->route('token') }}" />
82+
<form wire:submit="resetPassword" class="mt-8 space-y-6">
1183

1284
<div>
13-
<x-label for="email-address">
14-
{{ __('validation.attributes.email') }}
15-
</x-label>
1685
<x-filament::input.wrapper>
1786
<x-filament::input
1887
type="text"
1988
id="email-address"
2089
name="email"
90+
wire:model="email"
2191
autocomplete="email"
2292
required="true"
23-
:value="old('email', $request->email)"
93+
aria-label="{{ __('validation.attributes.email') }}"
94+
:placeholder="__('validation.attributes.email')"
2495
/>
2596
</x-filament::input.wrapper>
2697
</div>
2798

2899
<div>
29-
<x-label for="password">
30-
{{ __('validation.attributes.password') }}
31-
</x-label>
32100
<x-filament::input.wrapper>
33101
<x-filament::input
34102
type="password"
35103
id="password"
36104
name="password"
105+
wire:model="password"
37106
required="true"
107+
aria-label="{{ __('validation.attributes.password') }}"
108+
:placeholder="__('validation.attributes.password')"
38109
/>
39110
</x-filament::input.wrapper>
40111
</div>
41112

42113
<div>
43-
<x-label for="password_confirmation">
44-
{{ __('validation.attributes.password_confirmation') }}
45-
</x-label>
46114
<x-filament::input.wrapper>
47115
<x-filament::input
48116
type="password"
49117
id="password_confirmation"
50118
name="password_confirmation"
119+
wire:model="password_confirmation"
51120
required="true"
121+
aria-label="{{ __('validation.attributes.password_confirmation') }}"
122+
:placeholder="__('validation.attributes.password_confirmation')"
52123
/>
53124
</x-filament::input.wrapper>
54125
</div>
@@ -63,4 +134,4 @@
63134
</div>
64135

65136
<x-join-sponsors :title="__('global.sponsor_thanks')" />
66-
</x-app-layout>
137+
</div>

resources/views/livewire/pages/auth/verify-email.blade.php

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,41 @@
1-
<x-app-layout :title="__('pages/auth.verify.page_title')">
1+
<?php
2+
3+
use App\Livewire\Actions\Logout;
4+
use Illuminate\Support\Facades\Auth;
5+
use Illuminate\Support\Facades\Session;
6+
use Livewire\Attributes\Layout;
7+
use Livewire\Volt\Component;
8+
9+
new class extends Component
10+
{
11+
/**
12+
* Send an email verification notification to the user.
13+
*/
14+
public function sendVerification(): void
15+
{
16+
if (Auth::user()->hasVerifiedEmail()) {
17+
$this->redirectIntended(default: route('dashboard', absolute: false), navigate: true);
18+
19+
return;
20+
}
21+
22+
Auth::user()->sendEmailVerificationNotification();
23+
24+
Session::flash('status', 'verification-link-sent');
25+
}
26+
27+
/**
28+
* Log the current user out of the application.
29+
*/
30+
public function logout(Logout $logout): void
31+
{
32+
$logout();
33+
34+
$this->redirect('/', navigate: true);
35+
}
36+
}; ?>
37+
38+
<div>
239
<div class="flex min-h-screen flex-col items-center pt-6 sm:justify-center sm:pt-0">
340
<div class="mt-6 w-full sm:max-w-md lg:mt-10 lg:max-w-xl">
441
<p class="mb-4 text-center text-sm text-gray-500 dark:text-gray-400">
@@ -44,4 +81,4 @@ class="text-sm text-gray-500 dark:text-gray-400 underline hover:text-gray-900 fo
4481
</div>
4582

4683
<x-join-sponsors :title="__('global.sponsor_thanks')" />
47-
</x-app-layout>
84+
</div>
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
<?php
2+
3+
use App\Models\User;
4+
use Illuminate\Auth\Events\Verified;
5+
use Illuminate\Support\Facades\Event;
6+
use Illuminate\Support\Facades\URL;
7+
8+
test('email verification screen can be rendered', function () {
9+
$user = User::factory()->unverified()->create();
10+
11+
$response = $this->actingAs($user)->get('/verify-email');
12+
13+
$response->assertStatus(200);
14+
});
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 = $this->actingAs($user)->get($verificationUrl);
28+
29+
Event::assertDispatched(Verified::class);
30+
expect($user->fresh()->hasVerifiedEmail())->toBeTrue();
31+
$response->assertRedirect(route('dashboard', 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+
$this->actingAs($user)->get($verificationUrl);
44+
45+
expect($user->fresh()->hasVerifiedEmail())->toBeFalse();
46+
});
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
<?php
2+
3+
namespace Tests\Feature\Auth;
4+
5+
use App\Models\User;
6+
use Livewire\Volt\Volt;
7+
8+
test('confirm password screen can be rendered', function () {
9+
$user = User::factory()->create();
10+
11+
$response = $this->actingAs($user)->get('/confirm-password');
12+
13+
$response
14+
->assertSeeVolt('pages.auth.confirm-password')
15+
->assertStatus(200);
16+
});
17+
18+
test('password can be confirmed', function () {
19+
$user = User::factory()->create();
20+
21+
$this->actingAs($user);
22+
23+
$component = Volt::test('pages.auth.confirm-password')
24+
->set('password', 'password');
25+
26+
$component->call('confirmPassword');
27+
28+
$component
29+
->assertRedirect('/dashboard')
30+
->assertHasNoErrors();
31+
});
32+
33+
test('password is not confirmed with invalid password', function () {
34+
$user = User::factory()->create();
35+
36+
$this->actingAs($user);
37+
38+
$component = Volt::test('pages.auth.confirm-password')
39+
->set('password', 'wrong-password');
40+
41+
$component->call('confirmPassword');
42+
43+
$component
44+
->assertNoRedirect()
45+
->assertHasErrors('password');
46+
});
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
<?php
2+
3+
namespace Tests\Feature\Auth;
4+
5+
use App\Models\User;
6+
use Illuminate\Support\Facades\Hash;
7+
use Livewire\Volt\Volt;
8+
9+
test('password can be updated', function () {
10+
$user = User::factory()->create();
11+
12+
$this->actingAs($user);
13+
14+
$component = Volt::test('profile.update-password-form')
15+
->set('current_password', 'password')
16+
->set('password', 'new-password')
17+
->set('password_confirmation', 'new-password')
18+
->call('updatePassword');
19+
20+
$component
21+
->assertHasNoErrors()
22+
->assertNoRedirect();
23+
24+
$this->assertTrue(Hash::check('new-password', $user->refresh()->password));
25+
});
26+
27+
test('correct password must be provided to update password', function () {
28+
$user = User::factory()->create();
29+
30+
$this->actingAs($user);
31+
32+
$component = Volt::test('profile.update-password-form')
33+
->set('current_password', 'wrong-password')
34+
->set('password', 'new-password')
35+
->set('password_confirmation', 'new-password')
36+
->call('updatePassword');
37+
38+
$component
39+
->assertHasErrors(['current_password'])
40+
->assertNoRedirect();
41+
});

0 commit comments

Comments
 (0)