Pengaturan Keamanan
Security Settings adalah Account Page tempat user dapat:
- mengganti password;
- mengaktifkan TOTP;
- mengelola Passkey;
- mengaktifkan Email Code second factor.
Setiap Panel mendapatkan Page ini pada path miliknya sendiri dan di dalam shell Panel tersebut.
Gunakan dokumentasi ini untuk memahami:
- props yang dikirim;
- endpoint tujuan setiap card;
- dependency Fortify/application;
- serta behavior ketika feature tertentu dimatikan.
Contoh Minimal
Tidak ada registrasi tambahan:
<?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();
}
}2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Route:
php artisan route:list --name=panel.admin.pages.settings-securityGET admin/settings/security panel.admin.pages.settings-securityJika session belum melakukan password confirmation, user diarahkan terlebih dahulu ke:
password.confirmPage Class
PandaPanel\Pages\Settings\SecuritySettingsmerupakan regular Page.
| Member | Value |
|---|---|
$title | 'Security' |
$subheading | 'Password, two-factor authentication, and passkeys.' |
$slug | 'settings-security' |
$component | 'panel/settings/Security' |
$navigationIcon | 'shield' |
$navigationGroup | 'Account' |
$navigationSort | 20 |
$middleware | [RequirePassword::class] |
routePath() | 'settings/security' |
Contoh:
SecuritySettings::routeName(
'admin'
);
// panel.admin.pages.settings-security
SecuritySettings::url(
'admin'
);
// /admin/settings/security2
3
4
5
6
7
8
9
Password Confirmation
Protection ditempatkan di route middleware:
protected static array
$middleware = [
RequirePassword::class,
];2
3
4
Bukan di:
canAccess()karena canAccess() hanya dapat mengatakan:
boleh / tidak bolehsedangkan stale password session perlu:
redirect ke password confirmationApplication harus menyediakan view:
use Inertia\Inertia;
use Laravel\Fortify\Fortify;
Fortify::confirmPasswordView(
static fn () =>
Inertia::render(
'auth/ConfirmPassword'
)
);2
3
4
5
6
7
8
9
Tanpa view tersebut, Security Settings dapat mengarahkan user ke route yang tidak merender UI.
Props
| Prop | Type | Sumber | Selalu ada |
|---|---|---|---|
canManageTwoFactor | bool | Fortify | Ya |
canManagePasskeys | bool | Fortify | Ya |
passkeys | array | User passkeys | Ya |
passwordRules | string | PasswordRules::attribute() | Ya |
emailCodeEnabled | bool | EmailCodeFactor | Ya |
emailCodeUrls | array | Panel routes | Ya |
twoFactorEnabled | bool | Fortify TOTP state | Hanya jika TOTP feature aktif |
requiresConfirmation | bool | Fortify TOTP option | Hanya jika TOTP feature aktif |
Email Code URLs:
'emailCodeUrls' => [
'enable' =>
route(
$this
->panel()
->routeName(
'auth.two-factor.enable'
),
absolute: false
),
'disable' =>
route(
$this
->panel()
->routeName(
'auth.two-factor.disable'
),
absolute: false
),
],2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
TOTP check menggunakan:
method_exists(
$user,
'hasEnabledTwoFactorAuthentication'
)2
3
4
karena Fortify menyediakan trait, bukan interface.
Model tanpa trait dianggap TOTP tidak aktif dan tidak melempar exception.
Accessor untuk Fortify Trait
Security Settings menggunakan:
Laravel\Fortify\InteractsWithTwoFactorStateTrait tersebut membutuhkan:
public function user():
?Illuminate\Contracts\Auth\Authenticatable;
public function session():
Illuminate\Contracts\Session\Session;2
3
4
5
Implementasi Page:
$page =
new SecuritySettings;
$page->user();
$page->session();2
3
4
5
Jika Fortify TOTP management aktif, Page memanggil:
$this->ensureStateIsValid();setiap render.
Ini adalah repair mechanism Fortify untuk incomplete TOTP setup.
Jika user memulai setup tetapi meninggalkannya sebelum confirmation, state tersebut dapat dibersihkan kembali.
Passkey List
Data dibentuk server-side:
$user
->passkeys()
->select([
'id',
'name',
'credential',
'created_at',
'last_used_at',
])
->latest()
->get()
->map(
static fn (
$passkey
): array => [
'id' =>
$passkey->id,
'name' =>
$passkey->name,
'authenticator' =>
$passkey
->authenticator,
'created_at_diff' =>
$passkey
->created_at
->diffForHumans(),
'last_used_at_diff' =>
$passkey
->last_used_at
?->diffForHumans(),
]
)
->values()
->all();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
Credential tidak dikirim.
Dates dikirim dalam bentuk human-readable:
3 days agobukan raw timestamp.
Jika User Model bukan:
PasskeyUserlist:
[]Empat Bagian UI
Password
Form menggunakan application controller:
<Form
v-slot="{ errors, processing }"
v-bind="SecurityController.update.form()"
:options="{
preserveScroll: true,
}"
reset-on-success
:reset-on-error="[
'password',
'password_confirmation',
'current_password',
]"
>
<!--
current_password
password
password_confirmation
-->
</Form>2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Password write tetap menjadi tanggung jawab application.
TOTP
ManageTwoFactor.vue menggunakan Fortify:
POST /user/two-factor-authentication
DELETE /user/two-factor-authentication2
Card tidak dirender jika:
canManageTwoFactor = falsePasskeys
ManagePasskeys.vue menggunakan Fortify/laravel-passkeys.
Card tidak dirender jika:
canManagePasskeys = falseEmail Code
Email Code merupakan endpoint milik Panel:
<Form
v-slot="{ processing }"
:action="
emailCodeEnabled
? emailCodeUrls.disable
: emailCodeUrls.enable
"
method="post"
>
<Button
type="submit"
:disabled="processing"
>
{{
emailCodeEnabled
? 'Turn off'
: 'Turn on'
}}
</Button>
</Form>2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Enable dan disable sama-sama berada di balik:
RequirePasswordPanel Routes
| Route | Verb | Path | Extra Middleware |
|---|---|---|---|
panel.{id}.auth.two-factor.enable | POST | {panel}/two-factor/enable | RequirePassword |
panel.{id}.auth.two-factor.disable | POST | {panel}/two-factor/disable | RequirePassword |
panel.{id}.auth.two-factor.challenge | GET | {panel}/two-factor/challenge | — |
panel.{id}.auth.two-factor.send | POST | {panel}/two-factor/send | — |
panel.{id}.auth.two-factor.verify | POST | {panel}/two-factor/verify | — |
Password, TOTP, dan Passkey write bukan PandaBear endpoint.
Mematikan Settings
public function settings(
bool $settings = true
): self;
public function hasSettings():
bool;2
3
4
5
6
$panel->settings(
false
);2
3
Menghilangkan sekaligus:
Profile
Security
Appearance2
3
Jika ingin custom Security Settings, matikan built-in settings lalu register custom Page.
Dua Page dengan slug:
settings-securitymenghasilkan:
PanelRegistrationException::duplicatePageSlug()Testing
use Inertia\Testing\AssertableInertia;
it(
'guards the security page with password confirmation',
function (): void {
$user =
User::factory()
->create();
$this
->actingAs($user)
->get(
'/app/settings/security'
)
->assertRedirect(
route(
'password.confirm'
)
);
$this
->actingAs($user)
->withSession([
'auth.password_confirmed_at'
=> time(),
])
->get(
'/app/settings/security'
)
->assertOk()
->assertInertia(
fn (
AssertableInertia $page
) =>
$page
->component(
'panel/settings/Security'
)
->where(
'panel.id',
'app'
)
->has(
'passwordRules'
)
->has(
'emailCodeUrls.enable'
)
);
}
);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
47
48
49
50
51
Email Code enable/disable:
$this
->actingAs($user)
->withSession([
'auth.password_confirmed_at'
=> now()->timestamp,
])
->post(
'/app/two-factor/enable'
)
->assertRedirect();2
3
4
5
6
7
8
9
10
Hal yang Perlu Diperhatikan
- Security Settings Page hanya render; tidak melakukan write.
- POST Page menghasilkan 405.
- Password write menuju application SecurityController.
- TOTP dan Passkeys menuju Fortify.
- Email Code toggle adalah PandaBear endpoint.
password.confirmharus tersedia dan memiliki view.twoFactorEnableddanrequiresConfirmationtidak dikirim sama sekali jika Fortify TOTP feature mati.passwordRuleshanya browser hint.- Passkey list tidak dipaginate.
requireTwoFactor()saat ini mengecualikan semua standalone Page, bukan hanya Security Settings.settings(false)+requireTwoFactor()konflik; tanpa Security Page middleware akan fail-closed dengan 403.