Skip to content

Commit d3a51ba

Browse files
committed
feat: simple auth
1 parent 06f30af commit d3a51ba

33 files changed

+1553
-7
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,3 +24,5 @@ Homestead.yaml
2424
Thumbs.db
2525
auto-imports.d.ts
2626
components.d.ts
27+
routes
28+
wayfinder
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
<?php
2+
3+
namespace App\Actions\Fortify;
4+
5+
use App\Models\User;
6+
use Illuminate\Support\Facades\Hash;
7+
use Illuminate\Support\Facades\Validator;
8+
use Illuminate\Validation\Rule;
9+
use Laravel\Fortify\Contracts\CreatesNewUsers;
10+
11+
class CreateNewUser implements CreatesNewUsers
12+
{
13+
use PasswordValidationRules;
14+
15+
/**
16+
* Validate and create a newly registered user.
17+
*
18+
* @param array<string, string> $input
19+
*/
20+
public function create(array $input): User
21+
{
22+
Validator::make($input, [
23+
'name' => ['required', 'string', 'max:255'],
24+
'email' => [
25+
'required',
26+
'string',
27+
'email',
28+
'max:255',
29+
Rule::unique(User::class),
30+
],
31+
'password' => $this->passwordRules(),
32+
])->validate();
33+
34+
return User::create([
35+
'name' => $input['name'],
36+
'email' => $input['email'],
37+
'password' => Hash::make($input['password']),
38+
]);
39+
}
40+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<?php
2+
3+
namespace App\Actions\Fortify;
4+
5+
use Illuminate\Validation\Rules\Password;
6+
7+
trait PasswordValidationRules
8+
{
9+
/**
10+
* Get the validation rules used to validate passwords.
11+
*
12+
* @return array<int, \Illuminate\Contracts\Validation\Rule|array<mixed>|string>
13+
*/
14+
protected function passwordRules(): array
15+
{
16+
return ['required', 'string', Password::default(), 'confirmed'];
17+
}
18+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<?php
2+
3+
namespace App\Actions\Fortify;
4+
5+
use App\Models\User;
6+
use Illuminate\Support\Facades\Hash;
7+
use Illuminate\Support\Facades\Validator;
8+
use Laravel\Fortify\Contracts\ResetsUserPasswords;
9+
10+
class ResetUserPassword implements ResetsUserPasswords
11+
{
12+
use PasswordValidationRules;
13+
14+
/**
15+
* Validate and reset the user's forgotten password.
16+
*
17+
* @param array<string, string> $input
18+
*/
19+
public function reset(User $user, array $input): void
20+
{
21+
Validator::make($input, [
22+
'password' => $this->passwordRules(),
23+
])->validate();
24+
25+
$user->forceFill([
26+
'password' => Hash::make($input['password']),
27+
])->save();
28+
}
29+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
<?php
2+
3+
namespace App\Actions\Fortify;
4+
5+
use App\Models\User;
6+
use Illuminate\Support\Facades\Hash;
7+
use Illuminate\Support\Facades\Validator;
8+
use Laravel\Fortify\Contracts\UpdatesUserPasswords;
9+
10+
class UpdateUserPassword implements UpdatesUserPasswords
11+
{
12+
use PasswordValidationRules;
13+
14+
/**
15+
* Validate and update the user's password.
16+
*
17+
* @param array<string, string> $input
18+
*/
19+
public function update(User $user, array $input): void
20+
{
21+
Validator::make($input, [
22+
'current_password' => ['required', 'string', 'current_password:web'],
23+
'password' => $this->passwordRules(),
24+
], [
25+
'current_password.current_password' => __('The provided password does not match your current password.'),
26+
])->validateWithBag('updatePassword');
27+
28+
$user->forceFill([
29+
'password' => Hash::make($input['password']),
30+
])->save();
31+
}
32+
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
<?php
2+
3+
namespace App\Actions\Fortify;
4+
5+
use App\Models\User;
6+
use Illuminate\Contracts\Auth\MustVerifyEmail;
7+
use Illuminate\Support\Facades\Validator;
8+
use Illuminate\Validation\Rule;
9+
use Laravel\Fortify\Contracts\UpdatesUserProfileInformation;
10+
11+
class UpdateUserProfileInformation implements UpdatesUserProfileInformation
12+
{
13+
/**
14+
* Validate and update the given user's profile information.
15+
*
16+
* @param array<string, string> $input
17+
*/
18+
public function update(User $user, array $input): void
19+
{
20+
Validator::make($input, [
21+
'name' => ['required', 'string', 'max:255'],
22+
23+
'email' => [
24+
'required',
25+
'string',
26+
'email',
27+
'max:255',
28+
Rule::unique('users')->ignore($user->id),
29+
],
30+
])->validateWithBag('updateProfileInformation');
31+
32+
if ($input['email'] !== $user->email &&
33+
$user instanceof MustVerifyEmail) {
34+
$this->updateVerifiedUser($user, $input);
35+
} else {
36+
$user->forceFill([
37+
'name' => $input['name'],
38+
'email' => $input['email'],
39+
])->save();
40+
}
41+
}
42+
43+
/**
44+
* Update the given verified user's profile information.
45+
*
46+
* @param array<string, string> $input
47+
*/
48+
protected function updateVerifiedUser(User $user, array $input): void
49+
{
50+
$user->forceFill([
51+
'name' => $input['name'],
52+
'email' => $input['email'],
53+
'email_verified_at' => null,
54+
])->save();
55+
56+
$user->sendEmailVerificationNotification();
57+
}
58+
}

app/Http/Middleware/HandleInertiaRequests.php

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,12 @@ public function share(Request $request): array
3737
{
3838
return [
3939
...parent::share($request),
40-
//
40+
'flash' => [
41+
'status' => fn() => $request->session()->get('status'),
42+
],
43+
'auth' => $request->user() ? [
44+
'user' => $request->user(),
45+
] : null,
4146
];
4247
}
4348
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace App\Http\Responses;
6+
7+
use Laravel\Fortify\Contracts\FailedPasswordResetLinkRequestResponse as FailedPasswordResetLinkRequestResponseContract;
8+
use Laravel\Fortify\Contracts\SuccessfulPasswordResetLinkRequestResponse as SuccessfulPasswordResetLinkRequestResponseContract;
9+
10+
class PasswordResetLinkRequestResponse implements FailedPasswordResetLinkRequestResponseContract, SuccessfulPasswordResetLinkRequestResponseContract
11+
{
12+
public function toResponse($request)
13+
{
14+
return back()->with('status', 'If the email address is registered, you will receive an email with a reset link.');
15+
}
16+
}

app/Models/User.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,12 @@
22

33
namespace App\Models;
44

5-
// use Illuminate\Contracts\Auth\MustVerifyEmail;
5+
use Illuminate\Contracts\Auth\MustVerifyEmail;
66
use Illuminate\Database\Eloquent\Factories\HasFactory;
77
use Illuminate\Foundation\Auth\User as Authenticatable;
88
use Illuminate\Notifications\Notifiable;
99

10-
class User extends Authenticatable
10+
class User extends Authenticatable implements MustVerifyEmail
1111
{
1212
/** @use HasFactory<\Database\Factories\UserFactory> */
1313
use HasFactory, Notifiable;
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
<?php
2+
3+
namespace App\Providers;
4+
5+
use App\Actions\Fortify\CreateNewUser;
6+
use App\Actions\Fortify\ResetUserPassword;
7+
use App\Actions\Fortify\UpdateUserPassword;
8+
use App\Actions\Fortify\UpdateUserProfileInformation;
9+
use App\Http\Responses\PasswordResetLinkRequestResponse;
10+
use Illuminate\Cache\RateLimiting\Limit;
11+
use Illuminate\Http\Request;
12+
use Illuminate\Support\Facades\RateLimiter;
13+
use Illuminate\Support\ServiceProvider;
14+
use Illuminate\Support\Str;
15+
use Laravel\Fortify\Contracts\FailedPasswordResetLinkRequestResponse as FailedPasswordResetLinkRequestResponseContract;
16+
use Laravel\Fortify\Contracts\SuccessfulPasswordResetLinkRequestResponse as SuccessfulPasswordResetLinkRequestResponseContract;
17+
use Laravel\Fortify\Actions\RedirectIfTwoFactorAuthenticatable;
18+
use Laravel\Fortify\Fortify;
19+
20+
class FortifyServiceProvider extends ServiceProvider
21+
{
22+
/**
23+
* Register any application services.
24+
*/
25+
public function register(): void
26+
{
27+
$this->app->singleton(FailedPasswordResetLinkRequestResponseContract::class, PasswordResetLinkRequestResponse::class);
28+
29+
$this->app->singleton(SuccessfulPasswordResetLinkRequestResponseContract::class, PasswordResetLinkRequestResponse::class);
30+
}
31+
32+
/**
33+
* Bootstrap any application services.
34+
*/
35+
public function boot(): void
36+
{
37+
Fortify::createUsersUsing(CreateNewUser::class);
38+
Fortify::updateUserProfileInformationUsing(UpdateUserProfileInformation::class);
39+
Fortify::updateUserPasswordsUsing(UpdateUserPassword::class);
40+
Fortify::resetUserPasswordsUsing(ResetUserPassword::class);
41+
Fortify::redirectUserForTwoFactorAuthenticationUsing(RedirectIfTwoFactorAuthenticatable::class);
42+
43+
Fortify::loginView(function () {
44+
return inertia('auth/login');
45+
});
46+
47+
Fortify::registerView(function () {
48+
return inertia('auth/register');
49+
});
50+
51+
Fortify::verifyEmailView(function () {
52+
return inertia('auth/verify-email');
53+
});
54+
55+
Fortify::requestPasswordResetLinkView(function () {
56+
return inertia('auth/forgot-password');
57+
});
58+
59+
Fortify::resetPasswordView(function (Request $request) {
60+
return inertia('auth/reset-password', [
61+
'token' => $request->route('token'),
62+
'email' => $request->input('email'),
63+
]);
64+
});
65+
66+
Fortify::confirmPasswordView(function () {
67+
return inertia('auth/confirm-password');
68+
});
69+
70+
RateLimiter::for('login', function (Request $request) {
71+
$throttleKey = Str::transliterate(Str::lower($request->input(Fortify::username())).'|'.$request->ip());
72+
73+
return Limit::perMinute(5)->by($throttleKey);
74+
});
75+
76+
RateLimiter::for('two-factor', function (Request $request) {
77+
return Limit::perMinute(5)->by($request->session()->get('login.id'));
78+
});
79+
}
80+
}

0 commit comments

Comments
 (0)