import { Injectable } from '@nestjs/common'; import { PassportStrategy } from '@nestjs/passport'; import { ExtractJwt, Strategy } from 'passport-jwt'; import { ConfigService } from '@nestjs/config'; import { UserService } from '../user/user.service'; import { SafeUser } from '../user/dto/user-safe.dto'; // Import SafeUser @Injectable() export class JwtStrategy extends PassportStrategy(Strategy) { constructor( private configService: ConfigService, private userService: UserService, ) { super({ jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(), ignoreExpiration: false, secretOrKey: configService.get('JWT_SECRET')!, }); } // Passport first verifies the JWT's signature and expiration, then calls this method. async validate(payload: { sub: string; username: string; role?: string; tenantId?: string; }): Promise { // 1. ALWAYS lookup by ID (sub) for identity stability const user = await this.userService.findOneById(payload.sub); if (user) { // 2. ALWAYS prioritize database values for role/tenant to prevent stale token access return { id: user.id, username: user.username, role: user.role, // Use DB role tenantId: user.tenantId, // Use DB tenantId isAdmin: user.isAdmin, createdAt: user.createdAt, updatedAt: user.updatedAt, } as SafeUser; } return null; } }