Home Projects Portfolio Dashboard Export PDF Log in

Securing and Streamlining Reporting: RBAC and JWT in a NestJS Application

Project Context: Enhancing the sivbg-project

The sivbg-project aims to develop a robust platform, and a recent update focused on solidifying its security foundation and simplifying initial data reporting. This involved implementing a comprehensive Role-Based Access Control (RBAC) system, integrating JWT for authentication, and introducing a streamlined API for creating minimal reports without collecting sensitive personal data.

Establishing Secure Access with JWT

To manage access to protected resources, the sivbg-project backend, built with NestJS, adopted JSON Web Tokens (JWT) for authentication. For development purposes, a dedicated local login endpoint (POST /api/auth/dev-login) was introduced, allowing developers to easily obtain a JWT for testing authenticated routes. This mechanism will be replaced by a more robust authentication system for production environments.

Illustrative JWT Strategy (NestJS)

import { Injectable, UnauthorizedException } from '@nestjs/common';
import { PassportStrategy } from '@nestjs/passport';
import { Strategy, ExtractJwt } from 'passport-jwt';

interface JwtPayload {
  userId: string;
  roles: string[];
}

@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
  constructor() {
    super({
      jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
      ignoreExpiration: false,
      secretOrKey: 'YOUR_SECRET_KEY', // Replace with environment variable
    });
  }

  async validate(payload: JwtPayload) {
    // In a real app, validate user against DB
    if (!payload.userId) {
      throw new UnauthorizedException();
    }
    return { userId: payload.userId, roles: payload.roles };
  }
}

Implementing Role-Based Access Control (RBAC)

With JWT providing authentication, the next step was to implement a granular RBAC matrix to control what authenticated users can access. This is particularly crucial for protecting sensitive anonymized supervision data. For instance, users with the SUPERVISOR role are granted access to preview supervision data, while roles like VICTIM_WITNESS are explicitly denied, receiving a 403 Forbidden response.

Illustrative RBAC Guard (NestJS)

import { Injectable, CanActivate, ExecutionContext, ForbiddenException } from '@nestjs/common';
import { Reflector } from '@nestjs/core';

@Injectable()
export class RolesGuard implements CanActivate {
  constructor(private reflector: Reflector) {}

  canActivate(context: ExecutionContext): boolean {
    const requiredRoles = this.reflector.getAllAndOverride<string[]>('roles', [
      context.getHandler(),
      context.getClass(),
    ]);
    if (!requiredRoles) {
      return true; // No roles specified, access granted
    }
    const request = context.switchToHttp().getRequest();
    const user = request.user; // User object from JWT strategy

    if (!user || !user.roles) {
      throw new ForbiddenException('User has no roles assigned.');
    }

    const hasRole = () => requiredRoles.some(role => user.roles.includes(role));

    if (!hasRole()) {
        throw new ForbiddenException('You do not have the required roles to access this resource.');
    }

    return true;
  }
}

// Usage with a custom decorator:
// @SetMetadata('roles', ['SUPERVISOR'])
// @UseGuards(JwtAuthGuard, RolesGuard)
// @Get('supervision-preview')
// getSupervisionPreview() { /* ... */ }

Minimal Reporting for Sensitive Cases

A critical feature introduced is the POST /api/cases endpoint. This API allows for the creation of minimal reports for sensitive incidents without collecting any personally identifiable information (PII) such as names, addresses, or phone numbers. This design choice prioritizes privacy and simplifies the initial reporting process, generating a unique public reference for each case and recording consented orientations.

Illustrative Minimal Report DTO (NestJS)

import { IsString, IsArray, ArrayMinSize, IsOptional } from 'class-validator';

export class CreateCaseDto {
  @IsString()
  type: string; // e.g., 'physical', 'emotional'

  @IsArray()
  @ArrayMinSize(1)
  consentedOrientations: string[]; // e.g., ['legal_aid', 'medical_support']

  @IsOptional()
  @IsString()
  details?: string; // Minimal, non-identifying details
}

// Example controller snippet:
// @Post('cases')
// @UseGuards(JwtAuthGuard) // Only authenticated users can submit reports
// async createCase(@Body() createCaseDto: CreateCaseDto) {
//   const newCase = await this.casesService.createMinimalCase(createCaseDto);
//   return { reference: newCase.publicReference, message: 'Report created successfully.' };
// }

Frontend Integration with React

The React frontend was updated to seamlessly integrate with these new backend capabilities. This involved connecting the 'Access' screens to leverage the JWT authentication and RBAC, ensuring that users only see and interact with data relevant to their assigned roles. Additionally, the 'Reporting' screens were wired up to the POST /api/cases endpoint, providing a user-friendly interface for submitting minimal, privacy-centric reports.

Conclusion

This update to the sivbg-project significantly strengthens its security posture through JWT-based authentication and a robust RBAC system, while simultaneously enabling responsible and privacy-conscious data reporting. By separating development authentication from production needs and carefully designing the reporting API, the project lays a strong foundation for future growth while adhering to best practices in data protection.

Next Steps

Future work will focus on replacing the development-only login with a production-grade authentication provider, expanding the granularity of RBAC roles as needed, and further enhancing the reporting capabilities while strictly maintaining user privacy.


Generated with Gitvlg.com

Securing and Streamlining Reporting: RBAC and JWT in a NestJS Application
Seydina Limamou Laye Yade

Seydina Limamou Laye Yade

Author

Share: