fix(auth): toaster on auth pages + audit email-link resets
Codex review follow-ups: extract the notify toaster into a shared partial and include it in the auth layout too, so sendResetLink + recovery-code regenerate actually surface their confirmation (the auth layout had no toaster). Record an AuditEvent for password resets via the email-token path, matching the 2FA path. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>feat/v1-foundation
parent
3a67aa99ec
commit
2ff2cc34c0
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
namespace App\Livewire\Auth;
|
namespace App\Livewire\Auth;
|
||||||
|
|
||||||
|
use App\Models\AuditEvent;
|
||||||
use Illuminate\Auth\Events\PasswordReset;
|
use Illuminate\Auth\Events\PasswordReset;
|
||||||
use Illuminate\Support\Facades\Hash;
|
use Illuminate\Support\Facades\Hash;
|
||||||
use Illuminate\Support\Facades\Password;
|
use Illuminate\Support\Facades\Password;
|
||||||
|
|
@ -50,6 +51,14 @@ class ResetPassword extends Component
|
||||||
'remember_token' => Str::random(60),
|
'remember_token' => Str::random(60),
|
||||||
])->save();
|
])->save();
|
||||||
|
|
||||||
|
AuditEvent::create([
|
||||||
|
'user_id' => $user->id,
|
||||||
|
'actor' => $user->name ?? 'system',
|
||||||
|
'action' => 'password.reset',
|
||||||
|
'target' => $user->email,
|
||||||
|
'ip' => request()->ip(),
|
||||||
|
]);
|
||||||
|
|
||||||
event(new PasswordReset($user));
|
event(new PasswordReset($user));
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -40,28 +40,7 @@
|
||||||
</main>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{{-- Toaster: catches Livewire-dispatched `notify` browser events --}}
|
@include('partials.toaster')
|
||||||
<div
|
|
||||||
x-data="{ toasts: [] }"
|
|
||||||
x-on:notify.window="
|
|
||||||
const msg = $event.detail?.message ?? $event.detail?.[0]?.message ?? @js(__('shell.toast_done'));
|
|
||||||
const id = (window.crypto?.randomUUID?.() ?? String(Date.now() + Math.random()));
|
|
||||||
toasts.push({ id, msg });
|
|
||||||
setTimeout(() => { toasts = toasts.filter(t => t.id !== id) }, 4000);
|
|
||||||
"
|
|
||||||
class="pointer-events-none fixed inset-x-0 bottom-4 z-50 flex flex-col items-center gap-2 px-4 sm:items-end sm:px-6"
|
|
||||||
aria-live="polite"
|
|
||||||
>
|
|
||||||
<template x-for="t in toasts" :key="t.id">
|
|
||||||
<div
|
|
||||||
x-transition.opacity.duration.200ms
|
|
||||||
class="pointer-events-auto flex items-center gap-2.5 rounded-md border border-line bg-raised px-4 py-2.5 shadow-pop"
|
|
||||||
>
|
|
||||||
<span class="h-1.5 w-1.5 shrink-0 rounded-full bg-online"></span>
|
|
||||||
<span class="font-mono text-xs text-ink-2" x-text="t.msg"></span>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{{-- Command palette + keyboard shortcuts (persistent; one global keydown listener) --}}
|
{{-- Command palette + keyboard shortcuts (persistent; one global keydown listener) --}}
|
||||||
<x-command-palette />
|
<x-command-palette />
|
||||||
|
|
|
||||||
|
|
@ -78,6 +78,7 @@
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
|
@include('partials.toaster')
|
||||||
@livewireScripts
|
@livewireScripts
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
{{-- Toaster: catches Livewire-dispatched `notify` browser events. Shared by the app
|
||||||
|
and auth layouts so notifications surface on every page. --}}
|
||||||
|
<div
|
||||||
|
x-data="{ toasts: [] }"
|
||||||
|
x-on:notify.window="
|
||||||
|
const msg = $event.detail?.message ?? $event.detail?.[0]?.message ?? @js(__('shell.toast_done'));
|
||||||
|
const id = (window.crypto?.randomUUID?.() ?? String(Date.now() + Math.random()));
|
||||||
|
toasts.push({ id, msg });
|
||||||
|
setTimeout(() => { toasts = toasts.filter(t => t.id !== id) }, 4000);
|
||||||
|
"
|
||||||
|
class="pointer-events-none fixed inset-x-0 bottom-4 z-50 flex flex-col items-center gap-2 px-4 sm:items-end sm:px-6"
|
||||||
|
aria-live="polite"
|
||||||
|
>
|
||||||
|
<template x-for="t in toasts" :key="t.id">
|
||||||
|
<div
|
||||||
|
x-transition.opacity.duration.200ms
|
||||||
|
class="pointer-events-auto flex items-center gap-2.5 rounded-md border border-line bg-raised px-4 py-2.5 shadow-pop"
|
||||||
|
>
|
||||||
|
<span class="h-1.5 w-1.5 shrink-0 rounded-full bg-online"></span>
|
||||||
|
<span class="font-mono text-xs text-ink-2" x-text="t.msg"></span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
Loading…
Reference in New Issue