mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-22 12:46:42 +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
|
|
@ -818,6 +818,25 @@ export class AuthController {
|
|||
}
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// Security Events
|
||||
// =========================================================================
|
||||
|
||||
/**
|
||||
* Get user security events (audit log)
|
||||
*
|
||||
* Returns the authenticated user's security events ordered by most recent first.
|
||||
*/
|
||||
@Get('security-events')
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@ApiOperation({ summary: 'Get user security events (audit log)' })
|
||||
@ApiBearerAuth('JWT-auth')
|
||||
@ApiResponse({ status: 200, description: 'Returns security events' })
|
||||
@ApiResponse({ status: 401, description: 'Not authenticated' })
|
||||
async getSecurityEvents(@CurrentUser() user: CurrentUserData, @Req() req: Request) {
|
||||
return this.betterAuthService.getSecurityEvents(user.userId);
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// Passkey (WebAuthn) Endpoints
|
||||
// =========================================================================
|
||||
|
|
|
|||
|
|
@ -2117,4 +2117,29 @@ export class BetterAuthService {
|
|||
throw new UnauthorizedException('Failed to exchange session for tokens');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get security events for a user (audit log)
|
||||
*/
|
||||
async getSecurityEvents(userId: string, limit = 50) {
|
||||
const db = getDb(this.databaseUrl);
|
||||
const { securityEvents } = await import('../../db/schema');
|
||||
const { eq, desc } = await import('drizzle-orm');
|
||||
|
||||
const events = await db
|
||||
.select({
|
||||
id: securityEvents.id,
|
||||
eventType: securityEvents.eventType,
|
||||
ipAddress: securityEvents.ipAddress,
|
||||
userAgent: securityEvents.userAgent,
|
||||
metadata: securityEvents.metadata,
|
||||
createdAt: securityEvents.createdAt,
|
||||
})
|
||||
.from(securityEvents)
|
||||
.where(eq(securityEvents.userId, userId))
|
||||
.orderBy(desc(securityEvents.createdAt))
|
||||
.limit(limit);
|
||||
|
||||
return events;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue