From bbb6c97408199955720d5f770844f6417c5bafb6 Mon Sep 17 00:00:00 2001 From: LeoMortari Date: Mon, 15 Sep 2025 01:15:29 -0300 Subject: [PATCH] add more logs --- package.json | 2 + src/auth/keycloak.strategy.ts | 89 +++++++++++++++++++++++------------ src/main.ts | 39 +++++++++++++-- 3 files changed, 96 insertions(+), 34 deletions(-) diff --git a/package.json b/package.json index 6a532a7..1b63adb 100644 --- a/package.json +++ b/package.json @@ -33,8 +33,10 @@ "cross-env": "10.0.0", "dayjs": "1.11.13", "jwks-rsa": "3.2.0", + "nestjs-pino": "4.4.0", "passport": "0.7.0", "passport-jwt": "4.0.1", + "pino-http": "10.5.0", "reflect-metadata": "0.2.2", "rxjs": "7.8.1" }, diff --git a/src/auth/keycloak.strategy.ts b/src/auth/keycloak.strategy.ts index 6ab9723..5199a7a 100644 --- a/src/auth/keycloak.strategy.ts +++ b/src/auth/keycloak.strategy.ts @@ -52,41 +52,70 @@ export class KeycloakJwtStrategy extends PassportStrategy(Strategy, 'jwt') { private readonly logger = new Logger(KeycloakJwtStrategy.name); validate(payload: JwtPayload): JwtPayload { - this.logger.debug( - `JWT Payload received: ${JSON.stringify(payload, null, 2)}`, - ); + try { + // Basic JWT info + this.logger.verbose('=== JWT Validation Start ==='); - // Log important JWT claims - this.logger.debug(`JWT Subject (sub): ${payload.sub}`); - this.logger.debug(`JWT Issuer (iss): ${payload.iss}`); - this.logger.debug(`JWT Audience (aud): ${JSON.stringify(payload.aud)}`); - this.logger.debug( - `JWT Expiration (exp): ${new Date(payload.exp * 1000).toISOString()}`, - ); - this.logger.debug( - `JWT Issued At (iat): ${new Date(payload.iat * 1000).toISOString()}`, - ); + // Token metadata + this.logger.verbose(`Subject (sub): ${payload.sub}`); + this.logger.verbose(`Issuer (iss): ${payload.iss}`); + this.logger.verbose(`Audience (aud): ${JSON.stringify(payload.aud)}`); + this.logger.verbose( + `Issued At (iat): ${new Date(payload.iat * 1000).toISOString()}`, + ); + this.logger.verbose( + `Expiration (exp): ${new Date(payload.exp * 1000).toISOString()}`, + ); - // Log user info - this.logger.debug(`User email: ${payload.email}`); - this.logger.debug(`Username: ${payload.preferred_username}`); + // User info + this.logger.verbose('--- User Info ---'); + this.logger.verbose(`Email: ${payload.email || 'N/A'}`); + this.logger.verbose(`Username: ${payload.preferred_username || 'N/A'}`); + this.logger.verbose( + `Name: ${payload.given_name || ''} ${payload.family_name || ''}`.trim() || + 'N/A', + ); - // Log roles - this.logger.debug( - `Realm access roles: ${JSON.stringify(payload.realm_access?.roles || [])}`, - ); + // Realm roles + this.logger.verbose('--- Realm Access ---'); + if (payload.realm_access?.roles?.length) { + payload.realm_access.roles.forEach((role: string) => { + this.logger.verbose(`- ${role}`); + }); + } else { + this.logger.verbose('No realm roles found'); + } - if (payload.resource_access) { - this.logger.debug('Resource access:'); - Object.entries(payload.resource_access).forEach(([resource, data]) => { - this.logger.debug(` ${resource}: ${JSON.stringify(data.roles || [])}`); - }); + // Resource access + this.logger.verbose('--- Resource Access ---'); + if (payload.resource_access) { + Object.entries(payload.resource_access).forEach(([resource, data]) => { + if (data?.roles?.length) { + this.logger.verbose(`${resource} roles:`); + data.roles.forEach((role: string) => { + this.logger.verbose(` - ${role}`); + }); + } + }); + } else { + this.logger.verbose('No resource access found'); + } + + // Token expiration check + const now = Math.floor(Date.now() / 1000); + if (payload.exp < now) { + const minutesAgo = Math.round((now - payload.exp) / 60); + this.logger.warn(`Token expired ${minutesAgo} minutes ago`); + throw new UnauthorizedException('Token expirado'); + } + + this.logger.verbose('=== JWT Validation Successful ==='); + return payload; + } catch (error: unknown) { + const errorMessage = error instanceof Error ? error.stack : String(error); + this.logger.error('JWT Validation Error:', errorMessage); + throw error; } - - if (payload.exp < Date.now() / 1000) { - throw new UnauthorizedException('Token expirado'); - } - return payload; } } diff --git a/src/main.ts b/src/main.ts index b629495..9f19653 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,13 +1,44 @@ -import { ClassSerializerInterceptor } from '@nestjs/common'; +import { + ClassSerializerInterceptor, + Logger, + ValidationPipe, +} from '@nestjs/common'; import { NestFactory, Reflector } from '@nestjs/core'; import { AppModule } from './app.module'; +import { Logger as PinoLogger } from 'nestjs-pino'; async function bootstrap() { - const app = await NestFactory.create(AppModule); + const app = await NestFactory.create(AppModule, { + bufferLogs: true, + logger: ['error', 'warn', 'log', 'debug', 'verbose'], + }); + const reflector = app.get(Reflector); + const logger = new Logger('Bootstrap'); + + const pinoLogger = app.get(PinoLogger); + app.useLogger(pinoLogger); + + app.useGlobalPipes( + new ValidationPipe({ + whitelist: true, + transform: true, + }), + ); app.useGlobalInterceptors(new ClassSerializerInterceptor(reflector)); - await app.listen(process.env.PORT ?? 3000); + app.enableCors(); + + const port = process.env.PORT ?? 3000; + await app.listen(port); + + logger.log(`Application is running on port: ${port}`); + logger.debug(`Debug mode is enabled`); } -void bootstrap(); + +bootstrap().catch((err) => { + const logger = new Logger('Bootstrap'); + logger.error('Failed to start application', err); + process.exit(1); +});