Loading source
Pulling the file list, source metadata, and syntax-aware rendering for this listing.
Source from repo
40 prioritized NestJS best practices across architecture, DI, security, performance, testing, and microservices.
Files
Skill
Size
Entrypoint
Format
Open file
Syntax-highlighted preview of this file as included in the skill package.
rules/security-auth-jwt.md
1---2title: Implement Secure JWT Authentication3impact: CRITICAL4impactDescription: Essential for secure APIs5tags: security, jwt, authentication, tokens6---78## Implement Secure JWT Authentication910Use `@nestjs/jwt` with `@nestjs/passport` for authentication. Store secrets securely, use appropriate token lifetimes, implement refresh tokens, and validate tokens properly. Never expose sensitive data in JWT payloads.1112**Incorrect (insecure JWT implementation):**1314```typescript15// Hardcode secrets16@Module({17imports: [18JwtModule.register({19secret: 'my-secret-key', // Exposed in code20signOptions: { expiresIn: '7d' }, // Too long21}),22],23})24export class AuthModule {}2526// Store sensitive data in JWT27async login(user: User): Promise<{ accessToken: string }> {28const payload = {29sub: user.id,30email: user.email,31password: user.password, // NEVER include password!32ssn: user.ssn, // NEVER include sensitive data!33isAdmin: user.isAdmin, // Can be tampered if not verified34};35return { accessToken: this.jwtService.sign(payload) };36}3738// Skip token validation39@Injectable()40export class JwtStrategy extends PassportStrategy(Strategy) {41constructor() {42super({43jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),44secretOrKey: 'my-secret',45});46}4748async validate(payload: any): Promise<any> {49return payload; // No validation of user existence50}51}52```5354**Correct (secure JWT with refresh tokens):**5556```typescript57// Secure JWT configuration58@Module({59imports: [60JwtModule.registerAsync({61imports: [ConfigModule],62inject: [ConfigService],63useFactory: (config: ConfigService) => ({64secret: config.get<string>('JWT_SECRET'),65signOptions: {66expiresIn: '15m', // Short-lived access tokens67issuer: config.get<string>('JWT_ISSUER'),68audience: config.get<string>('JWT_AUDIENCE'),69},70}),71}),72PassportModule.register({ defaultStrategy: 'jwt' }),73],74})75export class AuthModule {}7677// Minimal JWT payload78@Injectable()79export class AuthService {80async login(user: User): Promise<TokenResponse> {81// Only include necessary, non-sensitive data82const payload: JwtPayload = {83sub: user.id,84email: user.email,85roles: user.roles,86iat: Math.floor(Date.now() / 1000),87};8889const accessToken = this.jwtService.sign(payload);90const refreshToken = await this.createRefreshToken(user.id);9192return { accessToken, refreshToken, expiresIn: 900 };93}9495private async createRefreshToken(userId: string): Promise<string> {96const token = randomBytes(32).toString('hex');97const hashedToken = await bcrypt.hash(token, 10);9899await this.refreshTokenRepo.save({100userId,101token: hashedToken,102expiresAt: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000), // 7 days103});104105return token;106}107}108109// Proper JWT strategy with validation110@Injectable()111export class JwtStrategy extends PassportStrategy(Strategy) {112constructor(113private config: ConfigService,114private usersService: UsersService,115) {116super({117jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),118secretOrKey: config.get<string>('JWT_SECRET'),119ignoreExpiration: false,120issuer: config.get<string>('JWT_ISSUER'),121audience: config.get<string>('JWT_AUDIENCE'),122});123}124125async validate(payload: JwtPayload): Promise<User> {126// Verify user still exists and is active127const user = await this.usersService.findById(payload.sub);128129if (!user || !user.isActive) {130throw new UnauthorizedException('User not found or inactive');131}132133// Verify token wasn't issued before password change134if (user.passwordChangedAt) {135const tokenIssuedAt = new Date(payload.iat * 1000);136if (tokenIssuedAt < user.passwordChangedAt) {137throw new UnauthorizedException('Token invalidated by password change');138}139}140141return user;142}143}144```145146Reference: [NestJS Authentication](https://docs.nestjs.com/security/authentication)147