fix(security): harden auth — rate limiting, admin audit logging, JWT aud/iss

- Add @Throttle (5 req/hour per IP) on register, login, refresh endpoints
- Add audit logging in RolesGuard for failed admin access attempts (userId, role, IP, action)
- Add audience ('goodgo-api') and issuer ('goodgo-platform') claims to JWT tokens
- Validate aud/iss in JwtStrategy to prevent cross-service token reuse

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
Ho Ngoc Hai
2026-04-08 06:17:02 +07:00
parent e60b95cdec
commit be0deddeed
4 changed files with 113 additions and 15 deletions

View File

@@ -1,10 +1,12 @@
import { Injectable, type CanActivate, type ExecutionContext } from '@nestjs/common';
import { Injectable, Logger, type CanActivate, type ExecutionContext } from '@nestjs/common';
import { Reflector } from '@nestjs/core';
import { type UserRole } from '@prisma/client';
import { ROLES_KEY } from '../decorators/roles.decorator';
@Injectable()
export class RolesGuard implements CanActivate {
private readonly logger = new Logger(RolesGuard.name);
constructor(private readonly reflector: Reflector) {}
canActivate(context: ExecutionContext): boolean {
@@ -17,7 +19,20 @@ export class RolesGuard implements CanActivate {
return true;
}
const { user } = context.switchToHttp().getRequest();
return requiredRoles.includes(user?.role);
const request = context.switchToHttp().getRequest();
const user = request.user;
const hasRole = requiredRoles.includes(user?.role);
if (!hasRole) {
const ip = request.ip || request.headers?.['x-forwarded-for'] || 'unknown';
const handler = context.getHandler().name;
const controller = context.getClass().name;
this.logger.warn(
`Access denied: userId=${user?.sub ?? 'unknown'}, role=${user?.role ?? 'none'}, ` +
`required=${requiredRoles.join(',')}, action=${controller}.${handler}, ip=${ip}`,
);
}
return hasRole;
}
}