Reset Password
PandaBear menyediakan dua guest Page dengan branding Panel:
- halaman untuk meminta reset link;
- halaman untuk mengatur password baru.
Kedua Page menggunakan path milik Panel tetapi mengirim form ke endpoint reset password milik Fortify.
Gunakan fitur ini agar user yang lupa password tidak perlu keluar dari visual shell/branding Panel selama recovery flow.
Contoh Minimal
<?php
declare(strict_types=1);
namespace App\Panels\Admin;
use PandaPanel\Core\Panel;
use PandaPanel\Core\PanelProvider;
final class AdminPanelProvider
extends PanelProvider
{
public function panel(
Panel $panel
): Panel {
return $panel
->path('admin')
->auth()
->login()
->passwordReset();
}
}2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
Routes:
php artisan route:list --name=panel.admin.auth.passwordGET admin/forgot-password
panel.admin.auth.password.request
GET admin/reset-password/{token}
panel.admin.auth.password.reset2
3
4
5
Login Page akan menampilkan:
Forgot your password?API
public function passwordReset(
bool $passwordReset = true
): self;
public function hasPasswordReset():
bool;2
3
4
5
6
Contoh:
use PandaPanel\Core\PanelManager;
$panel =
app(PanelManager::class)
->get('admin');
$panel->hasPasswordReset();
// true2
3
4
5
6
7
8
Tiga bagian membaca flag:
| Reader | Jika false |
|---|---|
| Route registrar | Kedua Page tidak diregistrasikan |
| Controller Page | 404 |
| Login Page | Tidak menampilkan Forgot Password link |
passwordReset() membutuhkan:
login()Tanpa Login Page, reset Page juga tidak diregistrasikan.
Parameter boolean dapat berupa expression:
$panel->passwordReset(
! app()->isProduction()
);2
3
Dua Page
Keduanya merupakan guest route:
base middleware
+
ResolvePanel2
3
tanpa auth.
Forgot Password
public function requestPasswordReset(
Request $request
): Response {
abort_unless(
$this
->panel()
->hasPasswordReset(),
404
);
return Inertia::render(
'panel/auth/ForgotPassword',
[
'panel' =>
$this
->panel()
->toSharedArray(),
'status' =>
$request
->session()
->get(
'status'
),
]
);
}2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
Props:
| Prop | Type | Sumber |
|---|---|---|
panel | PanelDefinition | Panel shared definition |
status | string|null | Fortify flash status |
Reset Password
public function resetPassword(
Request $request
): Response {
abort_unless(
$this
->panel()
->hasPasswordReset(),
404
);
return Inertia::render(
'panel/auth/ResetPassword',
[
'panel' =>
$this
->panel()
->toSharedArray(),
'email' =>
$request
->query(
'email'
),
'token' =>
(string)
$request
->route(
'token'
),
'passwordRules' =>
PasswordRules::attribute(),
]
);
}2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
Props:
| Prop | Type | Sumber |
|---|---|---|
panel | PanelDefinition | Panel |
email | string|null | Query string |
token | string | Route parameter |
passwordRules | string | PasswordRules::attribute() |
Endpoint Form
Kedua form menggunakan Fortify melalui Wayfinder.
Forgot:
<script setup lang="ts">
import { Form } from '@inertiajs/vue3';
import { email } from '@/routes/password';
</script>
<template>
<Form
v-slot="{ errors, processing }"
v-bind="email.form()"
>
<!-- email -->
</Form>
</template>2
3
4
5
6
7
8
9
10
11
12
13
Reset:
<script setup lang="ts">
import { Form } from '@inertiajs/vue3';
import { update } from '@/routes/password';
</script>
<template>
<Form
v-slot="{ errors, processing }"
v-bind="update.form()"
>
<input
type="hidden"
name="token"
:value="token"
/>
<!--
email,
password,
password_confirmation
-->
</Form>
</template>2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
| Wayfinder | Request | Fortify Route |
|---|---|---|
email | POST /forgot-password | password.email |
update | POST /reset-password | password.update |
Fortify tetap bertanggung jawab atas:
- throttle;
- token validation;
- PasswordReset event;
- session handling.
Semua Panel menggunakan endpoint yang sama.
Link Kembali ke Login
<TextLink
:href="`/${panel.path}/login`"
>
Back to log in
</TextLink>2
3
4
5
Path dibangun dari Panel path agar tetap benar jika Panel dipindahkan.
Password Policy
PasswordRules menerjemahkan Laravel Password policy menjadi browser:
passwordrulesAPI:
public static function attribute(
?Illuminate\Validation\Rules\Password $password = null
): string;2
3
Contoh:
PasswordRules::attribute();
// menggunakan Password::defaults()
PasswordRules::attribute(
Password::min(8)
);
// minlength: 8;
PasswordRules::attribute(
Password::min(12)
->mixedCase()
->numbers()
->symbols()
);
// minlength: 12;
// required: lower;
// required: upper;
// required: digit;
// required: special;2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Attribute tersebut hanya memengaruhi browser password suggestion.
Validation authoritative tetap berada di Fortify ResetUserPassword.
URL pada Email Reset
Ini bagian penting.
Laravel ResetPassword Notification secara default membuat URL berdasarkan application route:
password.resetbukan route Panel.
Jadi user yang meminta reset dari:
/admin/forgot-passwordsecara default dapat menerima email yang mengarah ke:
/reset-password/{token}bukan:
/admin/reset-password/{token}Untuk mengarahkannya ke Panel:
<?php
declare(strict_types=1);
namespace App\Providers;
use Illuminate\Auth\Notifications\ResetPassword;
use Illuminate\Support\ServiceProvider;
use PandaPanel\Core\PanelManager;
final class AppServiceProvider
extends ServiceProvider
{
public function boot(): void
{
ResetPassword::createUrlUsing(
static function (
object $notifiable,
string $token
): string {
$panel =
app(
PanelManager::class
)
->get(
'admin'
);
return route(
$panel
->routeName(
'auth.password.reset'
),
[
'token' =>
$token,
'email' =>
$notifiable
->getEmailForPasswordReset(),
]
);
}
);
}
}2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
email query parameter bukan decoration.
Reset Page membacanya dari URL dan Fortify membutuhkan email saat POST update password.
Jika application memiliki beberapa Panel, hanya ada satu ResetPassword notification callback.
Jika tujuan berbeda per account, branch berdasarkan data pada $notifiable, bukan current request karena queued mail tidak memiliki HTTP request context.
Setelah Password Reset
Fortify mengarahkan user ke configured:
password-reset redirectyang dapat fallback ke application Login Page.
Jika ingin kembali ke Panel Login:
// config/fortify.php
'redirects' => [
'password-reset'
=> '/admin/login',
],2
3
4
5
6
Login Page Panel membaca status, sehingga success message tetap dapat ditampilkan.
Testing
Forgot Page:
$this
->get(
'/admin/forgot-password'
)
->assertOk()
->assertInertia(
fn (
AssertableInertia $page
) =>
$page
->component(
'panel/auth/ForgotPassword'
)
->where(
'panel.id',
'admin'
)
);2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Reset Page:
$this
->get(
'/admin/reset-password/the-token?email=ada@example.com'
)
->assertOk()
->assertInertia(
fn (
AssertableInertia $page
) =>
$page
->component(
'panel/auth/ResetPassword'
)
->where(
'token',
'the-token'
)
->where(
'email',
'ada@example.com'
)
->has(
'passwordRules'
)
);2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
Panel yang tidak mengaktifkan fitur:
$this
->get(
'/app/forgot-password'
)
->assertNotFound();2
3
4
5
Hal yang Perlu Diperhatikan
- Reset Page tidak memvalidasi token pada GET. Token divalidasi ketika form dikirim ke Fortify.
- Email berasal dari query string, bukan dari token.
- Reset link harus membawa token dan email.
- Fortify feature hanya mengontrol link Login; Page memeriksa Panel flag.
- Jika Fortify feature mati tetapi Panel flag hidup, Page render namun POST route tidak ada.
canAccess()tetap berjalan pada guest Page melalui ResolvePanel.- Guest harus diizinkan jika Panel menggunakan restrictive access callback.
- Tidak ada throttle per Panel. Semua menggunakan throttle Fortify.
- Password reset berbeda dari password change. Password change dilakukan di Security Settings.