v0.14 is out: a mobile auth pack for Swift/iOS and Android, catching insecure token storage, cleartext traffic, and OAuth in embedded WebViews. Read more →
HIGH AI PREVALENCE: HIGH auth.nestjs.jwt-hardcoded-secret

A NestJS JwtModule is configured with a hard-coded secret (or secretOrPrivateKey) string literal.

Why AI tools produce this: AI coding tools generate this anti-pattern by default, it appears in a large share of AI-written auth code.

Why this matters

This key signs and verifies every access token: committed to git it is one search away from compromise, letting an attacker forge tokens for any user.

Read it from the environment instead. Use JwtModule.registerAsync with ConfigService (useFactory: (config) => ({ secret: config.get('JWT_SECRET') })) or secret: process.env.JWT_SECRET, and add the variable to .env.example with a placeholder. Rotate the leaked value out of source control.

VULNERABLE
vulnerable.ts
import { Module } from '@nestjs/common';
import { JwtModule, JwtService } from '@nestjs/jwt';

@Module({
  imports: [
    // ruleid: auth.nestjs.jwt-hardcoded-secret
    JwtModule.register({
      secret: 'super-secret-signing-key',
      signOptions: { expiresIn: '60s' },
    }),
  ],
})
export class AuthModule {}

@Module({
  imports: [
    JwtModule.registerAsync({
      // ruleid: auth.nestjs.jwt-hardcoded-secret
      useFactory: () => ({ secretOrPrivateKey: 'another-hardcoded-key' }),
    }),
  ],
})
export class OtherModule {}

// ruleid: auth.nestjs.jwt-hardcoded-secret
const service = new JwtService({ secret: 'inline-service-secret' });
SAFE
safe.ts
import { Module } from '@nestjs/common';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { JwtModule } from '@nestjs/jwt';

@Module({
  imports: [
    // ok: auth.nestjs.jwt-hardcoded-secret -- secret read from the environment
    JwtModule.register({
      secret: process.env.JWT_SECRET,
      signOptions: { expiresIn: '15m' },
    }),
    // ok: auth.nestjs.jwt-hardcoded-secret -- secret resolved via ConfigService
    JwtModule.registerAsync({
      imports: [ConfigModule],
      inject: [ConfigService],
      useFactory: (config: ConfigService) => ({
        secret: config.get<string>('JWT_SECRET'),
        signOptions: { expiresIn: '15m' },
      }),
    }),
  ],
})
export class AuthModule {}

// ok: auth.nestjs.jwt-hardcoded-secret -- an unrelated object that happens to
// have a `secret` key, not a JwtModule config
const featureFlags = { secret: 'not-a-signing-key-just-a-flag-name' };

Suppressing this rule

If a finding is a genuine false positive, scope the suppression to the exact line and leave a reason, never disable the rule project-wide. Disable directives are line-scoped by design.

// oauthlint-disable-next-line auth.nestjs.jwt-hardcoded-secret -- <reason>

References

https://docs.nestjs.com/security/authentication ↗https://cwe.mitre.org/data/definitions/798.html ↗