feat: Add login/logout audit logging (refs #10)
All checks were successful
Deploy to Staging / Build Images (pull_request) Successful in 4m42s
Deploy to Staging / Deploy to Staging (pull_request) Successful in 38s
Deploy to Staging / Verify Staging (pull_request) Successful in 7s
Deploy to Staging / Notify Staging Ready (pull_request) Successful in 6s
Deploy to Staging / Notify Staging Failure (pull_request) Has been skipped
All checks were successful
Deploy to Staging / Build Images (pull_request) Successful in 4m42s
Deploy to Staging / Deploy to Staging (pull_request) Successful in 38s
Deploy to Staging / Verify Staging (pull_request) Successful in 7s
Deploy to Staging / Notify Staging Ready (pull_request) Successful in 6s
Deploy to Staging / Notify Staging Failure (pull_request) Has been skipped
Backend: - Add login event logging to getUserStatus() controller method - Create POST /auth/track-logout endpoint for logout tracking Frontend: - Create useLogout hook that wraps Auth0 logout with audit tracking - Update all logout locations to use the new hook (SettingsPage, Layout, MobileSettingsScreen, useDeletion) Login events are logged when the frontend calls /auth/user-status after Auth0 callback. Logout events are logged via fire-and-forget call to /auth/track-logout before Auth0 logout. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -193,6 +193,9 @@ export class AuthController {
|
||||
* GET /api/auth/user-status
|
||||
* Get user status for routing decisions
|
||||
* Protected endpoint - requires JWT
|
||||
*
|
||||
* Note: This endpoint is called once per Auth0 callback (from CallbackPage/CallbackMobileScreen).
|
||||
* We log the login event here since it's the first authenticated request after Auth0 redirect.
|
||||
*/
|
||||
async getUserStatus(request: FastifyRequest, reply: FastifyReply) {
|
||||
try {
|
||||
@@ -200,6 +203,17 @@ export class AuthController {
|
||||
|
||||
const result = await this.authService.getUserStatus(userId);
|
||||
|
||||
// Log login event to audit trail (called once per Auth0 callback)
|
||||
const ipAddress = this.getClientIp(request);
|
||||
await auditLogService.info(
|
||||
'auth',
|
||||
userId,
|
||||
'User login',
|
||||
'user',
|
||||
userId,
|
||||
{ ipAddress }
|
||||
).catch(err => logger.error('Failed to log login audit event', { error: err }));
|
||||
|
||||
logger.info('User status retrieved', {
|
||||
userId: userId.substring(0, 8) + '...',
|
||||
emailVerified: result.emailVerified,
|
||||
@@ -287,4 +301,43 @@ export class AuthController {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* POST /api/auth/track-logout
|
||||
* Track user logout event for audit logging
|
||||
* Protected endpoint - requires JWT
|
||||
*
|
||||
* Called by frontend before Auth0 logout to capture the logout event.
|
||||
* Returns success even if audit logging fails (non-blocking).
|
||||
*/
|
||||
async trackLogout(request: FastifyRequest, reply: FastifyReply) {
|
||||
try {
|
||||
const userId = (request as any).user.sub;
|
||||
const ipAddress = this.getClientIp(request);
|
||||
|
||||
// Log logout event to audit trail
|
||||
await auditLogService.info(
|
||||
'auth',
|
||||
userId,
|
||||
'User logout',
|
||||
'user',
|
||||
userId,
|
||||
{ ipAddress }
|
||||
).catch(err => logger.error('Failed to log logout audit event', { error: err }));
|
||||
|
||||
logger.info('User logout tracked', {
|
||||
userId: userId.substring(0, 8) + '...',
|
||||
});
|
||||
|
||||
return reply.code(200).send({ success: true });
|
||||
} catch (error: any) {
|
||||
// Don't block logout on audit failure - always return success
|
||||
logger.error('Failed to track logout', {
|
||||
error,
|
||||
userId: (request as any).user?.sub,
|
||||
});
|
||||
|
||||
return reply.code(200).send({ success: true });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,4 +48,10 @@ export const authRoutes: FastifyPluginAsync = async (
|
||||
preHandler: [fastify.authenticate],
|
||||
handler: authController.requestPasswordReset.bind(authController),
|
||||
});
|
||||
|
||||
// POST /api/auth/track-logout - Track logout event for audit (requires JWT)
|
||||
fastify.post('/auth/track-logout', {
|
||||
preHandler: [fastify.authenticate],
|
||||
handler: authController.trackLogout.bind(authController),
|
||||
});
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user