NestJS Profiler
Tutorials

Auth/Security profiling

Capture the authenticated user, roles and JWT claims in the profiler's Security panel with nest-profiler-auth.

This tutorial shows how to add the auth collector to capture authentication context (Passport user, JWT claims, roles) in the Security panel.

Prerequisites

  • @eleven-labs/nest-profiler installed and configured
  • Passport (or any auth strategy that populates request.user)

Step 1 - Install the package

pnpm add @eleven-labs/nest-profiler-auth@alpha

Step 2 - Register the module

app.module.ts
import { ConditionalModule } from '@nestjs/config';
import { ProfilerModule } from '@eleven-labs/nest-profiler';
import { AuthCollectorModule } from '@eleven-labs/nest-profiler-auth';

const isProfilerEnabled = (env: NodeJS.ProcessEnv) => env['PROFILER_ENABLED'] === 'true';

@Module({
  imports: [
    ConditionalModule.registerWhen(ProfilerModule.forRoot({ isGlobal: true }), isProfilerEnabled),
    ConditionalModule.registerWhen(
      AuthCollectorModule.forRoot({ maskUserFields: ['password', 'refreshToken'] }),
      isProfilerEnabled,
    ),
  ],
})
export class AppModule {}

Step 3 - Set up authentication

The auth collector reads request.user automatically. Any guard or middleware that sets request.user will work - Passport, JWT guards, API key guards, etc.

Example with a minimal JWT guard:

jwt-auth.guard.ts
import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common';

@Injectable()
export class JwtAuthGuard implements CanActivate {
  canActivate(context: ExecutionContext): boolean {
    const request = context.switchToHttp().getRequest();
    const authHeader = request.headers['authorization'];

    if (authHeader?.startsWith('Bearer ')) {
      try {
        const [, payload] = authHeader.slice(7).split('.');
        request.user = JSON.parse(Buffer.from(payload, 'base64url').toString());
      } catch {
        /* invalid JWT */
      }
    }

    return true; // always allow - auth is for display only
  }
}

Apply the guard globally or per-route:

app.module.ts
import { APP_GUARD } from '@nestjs/core';

providers: [{ provide: APP_GUARD, useClass: JwtAuthGuard }],

Step 4 - Test it

# Without auth - Security tab shows "Anonymous"
curl http://localhost:3000/profile

# With a JWT - Security tab shows user info and claims
TOKEN=$(curl -s "http://localhost:3000/api/v1/auth/token" | jq -r .token)
curl -H "Authorization: Bearer $TOKEN" http://localhost:3000/profile

Open /_profiler/{token}Security tab.

Authenticated profile shows:

  • Status: Authenticated
  • User: { sub: "42", username: "demo_user", email: "demo@example.com" }
  • Roles: ["user"]
  • JWT Claims: full decoded payload

The toolbar badge shows the username (e.g., demo_user) or anon for unauthenticated requests.

Masking

Fields matching /password|secret|key|token|credential/i are automatically replaced with ***. Additional fields can be masked via maskUserFields:

AuthCollectorModule.forRoot({
  maskUserFields: ['refreshToken', 'apiKey'],
});

The JWT is decoded without signature verification - the displayed claims are for debugging only. Never rely on this data for authorization decisions in your application code.

Driving options from ConfigService

Use forRootAsync() to resolve maskUserFields from ConfigService (or any provider), gated per environment with ConditionalModule — the recommended way (for the enabled flag, see Enabling and disabling the profiler):

import { ConditionalModule, ConfigService } from '@nestjs/config';

ConditionalModule.registerWhen(
  AuthCollectorModule.forRootAsync({
    inject: [ConfigService],
    useFactory: (config: ConfigService) => ({
      maskUserFields: config.get<string[]>('profiler.maskUserFields') ?? [
        'password',
        'refreshToken',
      ],
    }),
  }),
  isProfilerEnabled,
);

How it works

The collector reads request.user and the Authorization header from the current CLS context and decodes the JWT payload (no signature verification) before applying the masking rules. See How it works on the package page.

Powered & maintained by

On this page