mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-23 14:46:43 +02:00
feat(auth): rate limit feedback, audit log UI, and E2E tests
Rate-limiting feedback: - LoginPage detects 429/account-locked errors and shows countdown timer - Submit button disabled during cooldown period Audit log: - GET /auth/security-events endpoint (JWT-protected) in auth controller - getSecurityEvents() in BetterAuthService + shared-auth client - AuditLog component with event type labels, relative dates, UA parsing - Integrated in ManaCore settings page E2E tests (passkey-2fa.e2e-spec.ts): - Passkey registration/authentication flow tests - Auth guard enforcement (protected vs public endpoints) - 2FA passthrough route existence tests - Edge cases (cross-user access, missing fields, token shape) CSRF note: Already covered by Better Auth (SameSite + HttpOnly + Trusted Origins). Token refresh already has 4-retry + offline detection. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
11ab265d55
commit
0dfd603892
9 changed files with 1061 additions and 2 deletions
|
|
@ -202,6 +202,12 @@ export const authStore = {
|
|||
return authService.renamePasskey(passkeyId, friendlyName);
|
||||
},
|
||||
|
||||
async getSecurityEvents() {
|
||||
const authService = getAuthService();
|
||||
if (!authService) return [];
|
||||
return authService.getSecurityEvents();
|
||||
},
|
||||
|
||||
/**
|
||||
* Sign in with email and password
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<script lang="ts">
|
||||
import { onMount } from 'svelte';
|
||||
import { Button, Input, Card, PageHeader, GlobalSettingsSection } from '@manacore/shared-ui';
|
||||
import { PasskeyManager, TwoFactorSetup } from '@manacore/shared-auth-ui';
|
||||
import { PasskeyManager, TwoFactorSetup, AuditLog } from '@manacore/shared-auth-ui';
|
||||
import { authStore } from '$lib/stores/auth.svelte';
|
||||
import { creditsService } from '$lib/api/credits';
|
||||
import type { CreditBalance } from '$lib/api/credits';
|
||||
|
|
@ -23,6 +23,10 @@
|
|||
// Credits data
|
||||
let creditBalance = $state<CreditBalance | null>(null);
|
||||
|
||||
// Security events
|
||||
let securityEvents = $state<any[]>([]);
|
||||
let securityEventsLoading = $state(false);
|
||||
|
||||
onMount(async () => {
|
||||
if (authStore.isAuthenticated) {
|
||||
try {
|
||||
|
|
@ -30,6 +34,10 @@
|
|||
passkeys = await authStore.listPasskeys();
|
||||
// Load user settings from server
|
||||
await userSettings.load();
|
||||
// Load security events
|
||||
securityEventsLoading = true;
|
||||
securityEvents = await authStore.getSecurityEvents();
|
||||
securityEventsLoading = false;
|
||||
} catch (e) {
|
||||
console.error('Failed to load data:', e);
|
||||
}
|
||||
|
|
@ -307,6 +315,22 @@
|
|||
</div>
|
||||
</Card>
|
||||
|
||||
<!-- Security Log Section -->
|
||||
<Card>
|
||||
<div class="p-6">
|
||||
<AuditLog
|
||||
events={securityEvents}
|
||||
loading={securityEventsLoading}
|
||||
onRefresh={async () => {
|
||||
securityEventsLoading = true;
|
||||
securityEvents = await authStore.getSecurityEvents();
|
||||
securityEventsLoading = false;
|
||||
}}
|
||||
primaryColor="#6366f1"
|
||||
/>
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
<!-- My Data & Danger Zone -->
|
||||
<Card>
|
||||
<div class="p-6">
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue