add more logs
This commit is contained in:
@@ -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"
|
||||
},
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
39
src/main.ts
39
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);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user