Skip to main content

Gate Watcher

The Gate Watcher tracks authorization checks in your NestJS application, monitoring access control decisions, permissions, and policy evaluations.

What Gets Captured

  • Gate/ability name
  • Action being checked
  • Subject/resource being accessed
  • Authorization result (allowed/denied)
  • User ID making the request
  • Denial reason (if applicable)
  • Check duration
  • Authorization context

Configuration

NestLensModule.forRoot({
watchers: {
gate: {
enabled: true,
captureContext: true,
ignoreAbilities: ['viewDashboard', 'accessPublic'],
},
},
})

Configuration Options

OptionTypeDefaultDescription
enabledbooleanfalseEnable/disable gate tracking
captureContextbooleantrueCapture authorization context
ignoreAbilitiesstring[][]Abilities to ignore

Payload Structure

interface GateEntry {
type: 'gate';
payload: {
gate: string; // Gate/ability name
action: string; // Action (e.g., 'read', 'write')
subject?: string; // Resource/subject name
allowed: boolean; // Authorization result
userId?: string | number; // User making request
reason?: string; // Denial reason
duration: number; // Check time (ms)
context?: Record<string, unknown>; // Additional context
};
}

Usage Example

Provide Gate Service

import { NESTLENS_GATE_SERVICE } from 'nestlens';

@Module({
providers: [
AuthorizationService,
{
provide: NESTLENS_GATE_SERVICE,
useExisting: AuthorizationService,
},
],
})
export class AppModule {}

Authorization Service

@Injectable()
export class AuthorizationService {
async can(
gate: string,
action: string,
subject?: any,
user?: any,
): Promise<boolean> {
// Authorization logic
// Automatically tracked by NestLens
return this.checkPermission(gate, action, subject, user);
}

async allows(gate: string, user: User): Promise<boolean> {
return await this.can(gate, 'access', null, user);
}

async denies(gate: string, user: User): Promise<boolean> {
return !(await this.allows(gate, user));
}
}

Using Guards

@Injectable()
export class AbilityGuard implements CanActivate {
constructor(
private reflector: Reflector,
private authService: AuthorizationService,
) {}

async canActivate(context: ExecutionContext): Promise<boolean> {
const ability = this.reflector.get<string>('ability', context.getHandler());
if (!ability) return true;

const request = context.switchToHttp().getRequest();
const user = request.user;

// This check is tracked
return await this.authService.can(ability, 'execute', null, user);
}
}

// Use in controllers
@Controller('posts')
@UseGuards(AbilityGuard)
export class PostController {
@Get()
@SetMetadata('ability', 'viewPosts')
async findAll() {
return this.postService.findAll();
}

@Post()
@SetMetadata('ability', 'createPost')
async create(@Body() dto: CreatePostDto) {
return this.postService.create(dto);
}

@Delete(':id')
@SetMetadata('ability', 'deletePost')
async remove(@Param('id') id: string) {
return this.postService.remove(id);
}
}

Resource-Based Authorization

@Injectable()
export class PostAuthorizationService {
async canUpdatePost(user: User, post: Post): Promise<boolean> {
// Tracked with subject: 'Post', action: 'update'
if (user.id === post.authorId) return true;
if (user.role === 'admin') return true;
return false;
}

async canDeletePost(user: User, post: Post): Promise<boolean> {
return user.id === post.authorId || user.role === 'admin';
}
}

@Controller('posts')
export class PostController {
@Put(':id')
async update(
@Param('id') id: string,
@Body() dto: UpdatePostDto,
@User() user: User,
) {
const post = await this.postService.findOne(id);

// Authorization check tracked
const canUpdate = await this.authService.canUpdatePost(user, post);
if (!canUpdate) {
throw new ForbiddenException('Cannot update this post');
}

return this.postService.update(id, dto);
}
}

Dashboard View

In the NestLens dashboard, gate entries show:

  • Authorization check timeline
  • Allow/deny rates
  • Most checked abilities
  • Denied access attempts
  • User access patterns
  • Failed authorization analysis

Manual Tracking

For custom authorization logic:

import { GateWatcher } from 'nestlens';

@Injectable()
export class CustomAuthService {
constructor(private gateWatcher: GateWatcher) {}

async checkAccess(user: User, resource: string): Promise<boolean> {
const allowed = this.evaluatePermission(user, resource);

// Manually track the check
this.gateWatcher.trackCheck(
'customGate',
'access',
resource,
allowed,
user,
allowed ? undefined : 'Insufficient permissions',
);

return allowed;
}
}

Role-Based Access Control (RBAC)

@Injectable()
export class RBACService {
async can(action: string, subject: string, user: User): Promise<boolean> {
const permission = `${subject}:${action}`;
const allowed = user.roles.some(role =>
role.permissions.includes(permission),
);

// Tracked automatically
return allowed;
}
}

// Usage
const canEdit = await this.rbac.can('edit', 'users', user);
// Tracked as: gate: 'users', action: 'edit', allowed: true/false

Ignoring Abilities

Filter out noisy or internal checks:

NestLensModule.forRoot({
watchers: {
gate: {
ignoreAbilities: [
'viewDashboard', // Internal dashboard
'accessPublic', // Public resources
'healthCheck', // Health check endpoint
],
},
},
})