Skip to content

Commit 9c55858

Browse files
author
Tony Lea
committed
Adding forgot password and reset password functinality
1 parent 7991423 commit 9c55858

File tree

13 files changed

+330
-61
lines changed

13 files changed

+330
-61
lines changed

app/Http/Controllers/Auth/AuthenticatedSessionController.php

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,9 @@ class AuthenticatedSessionController extends Controller
1919
*/
2020
public function create(): Response
2121
{
22-
[$message, $author] = str(Inspiring::quotes()->random())->explode('-');
23-
2422
return Inertia::render('Auth/Login', [
2523
'canResetPassword' => Route::has('password.request'),
26-
'status' => session('status'),
27-
'name' => config('app.name'),
28-
'quote' => ['message' => trim($message), 'author' => trim($author)],
24+
'status' => session('status')
2925
]);
3026
}
3127

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
<?php
2+
3+
namespace App\Http\Controllers\Auth;
4+
5+
use App\Http\Controllers\Controller;
6+
use Illuminate\Auth\Events\PasswordReset;
7+
use Illuminate\Http\RedirectResponse;
8+
use Illuminate\Http\Request;
9+
use Illuminate\Support\Facades\Hash;
10+
use Illuminate\Support\Facades\Password;
11+
use Illuminate\Support\Str;
12+
use Illuminate\Validation\Rules;
13+
use Illuminate\Validation\ValidationException;
14+
use Inertia\Inertia;
15+
use Inertia\Response;
16+
17+
class NewPasswordController extends Controller
18+
{
19+
/**
20+
* Display the password reset view.
21+
*/
22+
public function create(Request $request): Response
23+
{
24+
return Inertia::render('Auth/ResetPassword', [
25+
'email' => $request->email,
26+
'token' => $request->route('token'),
27+
]);
28+
}
29+
30+
/**
31+
* Handle an incoming new password request.
32+
*
33+
* @throws \Illuminate\Validation\ValidationException
34+
*/
35+
public function store(Request $request): RedirectResponse
36+
{
37+
$request->validate([
38+
'token' => 'required',
39+
'email' => 'required|email',
40+
'password' => ['required', 'confirmed', Rules\Password::defaults()],
41+
]);
42+
43+
// Here we will attempt to reset the user's password. If it is successful we
44+
// will update the password on an actual user model and persist it to the
45+
// database. Otherwise we will parse the error and return the response.
46+
$status = Password::reset(
47+
$request->only('email', 'password', 'password_confirmation', 'token'),
48+
function ($user) use ($request) {
49+
$user->forceFill([
50+
'password' => Hash::make($request->password),
51+
'remember_token' => Str::random(60),
52+
])->save();
53+
54+
event(new PasswordReset($user));
55+
}
56+
);
57+
58+
// If the password was successfully reset, we will redirect the user back to
59+
// the application's home authenticated view. If there is an error we can
60+
// redirect them back to where they came from with their error message.
61+
if ($status == Password::PASSWORD_RESET) {
62+
return redirect()->route('login')->with('status', __($status));
63+
}
64+
65+
throw ValidationException::withMessages([
66+
'email' => [trans($status)],
67+
]);
68+
}
69+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
<?php
2+
3+
namespace App\Http\Controllers\Auth;
4+
5+
use App\Http\Controllers\Controller;
6+
use Illuminate\Http\RedirectResponse;
7+
use Illuminate\Http\Request;
8+
use Illuminate\Support\Facades\Password;
9+
use Illuminate\Validation\ValidationException;
10+
use Inertia\Inertia;
11+
use Inertia\Response;
12+
13+
class PasswordResetLinkController extends Controller
14+
{
15+
/**
16+
* Display the password reset link request view.
17+
*/
18+
public function create(): Response
19+
{
20+
return Inertia::render('Auth/ForgotPassword', [
21+
'status' => session('status'),
22+
]);
23+
}
24+
25+
/**
26+
* Handle an incoming password reset link request.
27+
*
28+
* @throws \Illuminate\Validation\ValidationException
29+
*/
30+
public function store(Request $request): RedirectResponse
31+
{
32+
$request->validate([
33+
'email' => 'required|email',
34+
]);
35+
36+
// We will send the password reset link to this user. Once we have attempted
37+
// to send the link, we will examine the response then see the message we
38+
// need to show to the user. Finally, we'll send out a proper response.
39+
$status = Password::sendResetLink(
40+
$request->only('email')
41+
);
42+
43+
if ($status == Password::RESET_LINK_SENT) {
44+
return back()->with('status', __($status));
45+
}
46+
47+
throw ValidationException::withMessages([
48+
'email' => [trans($status)],
49+
]);
50+
}
51+
}

app/Http/Controllers/Auth/RegisteredUserController.php

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,7 @@ class RegisteredUserController extends Controller
2121
*/
2222
public function create(): Response
2323
{
24-
[$message, $author] = str(Inspiring::quotes()->random())->explode('-');
25-
26-
return Inertia::render('Auth/Register', [
27-
'name' => config('app.name'),
28-
'quote' => ['message' => trim($message), 'author' => trim($author)]
29-
]);
24+
return Inertia::render('Auth/Register');
3025
}
3126

3227
/**

app/Http/Middleware/HandleInertiaRequests.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
namespace App\Http\Middleware;
44

5+
use Illuminate\Foundation\Inspiring;
56
use Illuminate\Http\Request;
67
use Inertia\Middleware;
78

@@ -35,11 +36,15 @@ public function version(Request $request): ?string
3536
*/
3637
public function share(Request $request): array
3738
{
39+
[$message, $author] = str(Inspiring::quotes()->random())->explode('-');
40+
3841
return array_merge(parent::share($request), [
3942
...parent::share($request),
4043
'auth' => [
4144
'user' => $request->user(),
4245
],
46+
'name' => config('app.name'),
47+
'quote' => ['message' => trim($message), 'author' => trim($author)]
4348
]);
4449
}
4550
}

resources/js/Layouts/AuthLayout.tsx

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,23 @@
1-
import { Link } from "@inertiajs/react";
1+
import { Link, usePage } from "@inertiajs/react";
22

33
// Components
44
import ApplicationLogo from "@/Components/ApplicationLogo";
55

6-
interface Quote {
7-
message: string;
8-
author: string;
9-
}
10-
116
interface AuthLayoutProps {
127
children: React.ReactNode;
8+
title?: string;
9+
description?: string;
1310
name?: string;
14-
quote?: Quote;
1511
}
1612

1713
export default function AuthLayout({
1814
children,
19-
name,
20-
quote
15+
title,
16+
description
2117
}: AuthLayoutProps) {
18+
const name = usePage().props.name;
19+
const quote = usePage().props.quote;
20+
2221
return (
2322
<div className="px-8 sm:px-0 relative h-dvh flex-col items-center justify-center grid lg:max-w-none lg:grid-cols-2 lg:px-0">
2423
<div className="relative hidden h-full flex-col bg-muted p-10 text-white dark:border-r lg:flex">
@@ -49,6 +48,12 @@ export default function AuthLayout({
4948
>
5049
<ApplicationLogo className="h-10 sm:h-12 fill-current text-black" />
5150
</Link>
51+
<div className="flex flex-col items-start sm:items-center gap-2 text-left sm:text-center">
52+
<h1 className="text-2xl font-bold">{title}</h1>
53+
<p className="text-balance text-sm text-muted-foreground">
54+
{description}
55+
</p>
56+
</div>
5257
{children}
5358
</div>
5459
</div>
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
// Components
2+
import InputError from "@/Components/InputError";
3+
import AuthLayout from "@/Layouts/AuthLayout";
4+
import { Button } from "@/components/ui/button";
5+
import { Input } from "@/components/ui/input";
6+
import { Label } from "@/components/ui/label";
7+
8+
import { Head, useForm, Link } from '@inertiajs/react';
9+
import { FormEventHandler } from 'react';
10+
11+
export default function ForgotPassword({ status }: { status?: string }) {
12+
const { data, setData, post, processing, errors } = useForm({
13+
email: '',
14+
});
15+
16+
const submit: FormEventHandler = (e) => {
17+
e.preventDefault();
18+
19+
post(route('password.email'));
20+
};
21+
22+
return (
23+
<AuthLayout
24+
title="Forgot Password"
25+
description="Enter your email to receive a password reset link"
26+
>
27+
<Head title="Forgot Password" />
28+
29+
{status && (
30+
<div className="mb-4 text-sm font-medium text-green-600">
31+
{status}
32+
</div>
33+
)}
34+
35+
<form onSubmit={submit}>
36+
<div className="grid gap-2">
37+
<Label htmlFor="email">Email Address</Label>
38+
<Input
39+
id="email"
40+
type="email"
41+
name="email"
42+
value={data.email}
43+
autoFocus
44+
onChange={(e) => setData('email', e.target.value)}
45+
/>
46+
47+
<InputError message={errors.email} className="mt-2" />
48+
</div>
49+
50+
<div className="mt-4 flex items-center justify-start">
51+
<Button className="w-full" disabled={processing}>
52+
Email Password Reset Link
53+
</Button>
54+
</div>
55+
<hr />
56+
</form>
57+
<div className="text-center text-sm space-x-1">
58+
<Link href={route("login")} className="underline underline-offset-4">
59+
Click here
60+
</Link>
61+
<span>to return back to the login page</span>
62+
</div>
63+
</AuthLayout>
64+
);
65+
}

resources/js/Pages/Auth/Login.tsx

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -18,15 +18,11 @@ interface LoginForm {
1818
interface LoginProps {
1919
status?: string;
2020
canResetPassword: boolean;
21-
name?: string;
22-
quote?: { author: string; content: string };
2321
}
2422

2523
export default function Login({
2624
status,
27-
canResetPassword,
28-
name,
29-
quote
25+
canResetPassword
3026
}: LoginProps) {
3127
const { data, setData, post, processing, errors, reset } = useForm<LoginForm>({
3228
email: "",
@@ -42,20 +38,18 @@ export default function Login({
4238
};
4339

4440
return (
45-
<AuthLayout name={name} quote={quote}>
41+
<AuthLayout
42+
title="Login to your account"
43+
description="Enter your email and password below to login"
44+
>
4645
<Head title="Log in" />
4746

4847
{status && (
4948
<div className="mb-4 text-sm font-medium text-green-600">
5049
{status}
5150
</div>
5251
)}
53-
<div className="flex flex-col items-start sm:items-center gap-2 text-left sm:text-center">
54-
<h1 className="text-2xl font-bold">Login to your account</h1>
55-
<p className="text-balance text-sm text-muted-foreground">
56-
Enter your email and password below to login
57-
</p>
58-
</div>
52+
5953
<form className="flex flex-col gap-6" onSubmit={submit}>
6054
<div className="grid gap-6">
6155
<div className="grid gap-2">

resources/js/Pages/Auth/Register.tsx

Lines changed: 6 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,7 @@ interface RegisterForm {
1616
password_confirmation: string;
1717
}
1818

19-
interface RegisterProps {
20-
name?: string;
21-
quote?: { author: string; content: string };
22-
}
23-
24-
export default function Register({
25-
name,
26-
quote
27-
}: RegisterProps) {
19+
export default function Register() {
2820
const { data, setData, post, processing, errors, reset } = useForm<RegisterForm>({
2921
name: "",
3022
email: "",
@@ -40,14 +32,11 @@ export default function Register({
4032
};
4133

4234
return (
43-
<AuthLayout name={name} quote={quote}>
35+
<AuthLayout
36+
title="Create an account"
37+
description="Enter your name, email and password below."
38+
>
4439
<Head title="Register" />
45-
<div className="flex flex-col items-start sm:items-center gap-2 text-left sm:text-center">
46-
<h1 className="text-2xl font-bold">Create an account</h1>
47-
<p className="text-balance text-sm text-muted-foreground">
48-
Enter your name, email and password below.
49-
</p>
50-
</div>
5140
<form className="flex flex-col gap-6" onSubmit={submit}>
5241
<div className="grid gap-6">
5342
<div className="grid gap-2">
@@ -61,7 +50,7 @@ export default function Register({
6150
onChange={(e) => setData("name", e.target.value)}
6251
disabled={processing}
6352
/>
64-
<InputError message={errors.name} />
53+
<InputError message={errors.name} className="mt-2" />
6554
</div>
6655

6756
<div className="grid gap-2">

0 commit comments

Comments
 (0)