SociApp implements role-based access control using the categoria field in the user entity. Different user categories have different levels of access to application features.
Available User Categories
User roles are defined in the categoria field of the user entity:
// backend/src/users/user.entity.ts:54
@ Column ()
categoria : string ;
The application supports these categories:
Admin / Administrador Full access to all features including user management, statistics, and system configuration
Monitor Access to dashboard statistics and monitoring features
Trabajador Worker role with limited access to specific features
Usuario Standard user with basic access
User Entity Structure
The complete user entity includes role and membership information:
// backend/src/users/user.entity.ts:1
import { Entity , PrimaryGeneratedColumn , Column } from 'typeorm' ;
@ Entity ( 'usuarios' )
export class Usuarios {
@ PrimaryGeneratedColumn ()
IdUsuario : number ;
@ Column ()
nombre : string ;
@ Column ()
apellidos : string ;
@ Column ()
password : string ;
@ Column ()
dni : string ;
@ Column ()
direccion : string ;
@ Column ({ name: 'CP' })
CP : string ;
@ Column ()
provincia : string ;
@ Column ()
poblacion : string ;
@ Column ()
pais : string ;
@ Column ()
email : string ;
@ Column ()
telefono : string ;
@ Column ({ type: 'date' })
fechaalta : Date ;
@ Column ({ type: 'date' , nullable: true })
fechabaja : Date ;
@ Column ()
formadepago : string ;
@ Column ({ type: 'decimal' })
cuota : number ;
@ Column ()
categoria : string ;
@ Column ({
type: 'enum' ,
enum: [ 'Socio' , 'NoSocio' ],
default: 'NoSocio'
})
socio : 'Socio' | 'NoSocio' ;
@ Column ({ default: false })
isVerified : boolean ;
@ Column ({ type: 'varchar' , length: 10 , nullable: true })
verificationCode : string | null ;
@ Column ({ type: 'timestamp' , nullable: true })
verificationExpires : Date | null ;
}
Membership Status
In addition to categories, users have a membership status:
// backend/src/users/user.entity.ts:56
@ Column ({
type: 'enum' ,
enum: [ 'Socio' , 'NoSocio' ],
default: 'NoSocio'
})
socio : 'Socio' | 'NoSocio' ;
Socio : Full members with active membership
NoSocio : Non-members or inactive memberships
Role-Based Access Control
The Roles Decorator
Use the @Roles() decorator to specify which categories can access an endpoint:
// backend/src/auth/roles.decorator.ts:1
import { SetMetadata } from '@nestjs/common' ;
export const Roles = ( ... roles : string []) => SetMetadata ( 'roles' , roles );
The Roles Guard
The RolesGuard enforces role-based access control:
// backend/src/auth/roles.guard.ts:1
import { Injectable , CanActivate , ExecutionContext } 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 . get < string []>( 'roles' , context . getHandler ());
if ( ! requiredRoles ) {
return true ;
}
const { user } = context . switchToHttp (). getRequest ();
if ( ! user ) {
return false ;
}
// Verify that the user has the required category
const userCategory = user . categoria ?. toLowerCase () || '' ;
// Normalize different variants of administrator
const normalizedCategory = userCategory === 'administrador' ? 'admin' : userCategory ;
// Check if the user has any of the required roles
const hasAccess = requiredRoles . some (( role ) => {
const normalizedRole = role . toLowerCase ();
return normalizedCategory === normalizedRole ||
( normalizedRole === 'admin' && normalizedCategory === 'administrador' ) ||
( normalizedCategory === 'admin' && normalizedRole === 'administrador' );
});
return hasAccess ;
}
}
Key Features
Case-Insensitive Matching
The guard normalizes both user categories and required roles to lowercase for comparison.
The guard treats ‘admin’ and ‘administrador’ as equivalent, supporting both Spanish and English naming conventions.
Endpoints can require multiple roles using the some() method - users need to match at least one.
Protecting Endpoints
Single Role Protection
Protect an endpoint for a single role:
import { Controller , Get , UseGuards } from '@nestjs/common' ;
import { JwtAuthGuard } from '../auth/jwt-auth.guard' ;
import { RolesGuard } from '../auth/roles.guard' ;
import { Roles } from '../auth/roles.decorator' ;
@ Controller ( 'admin' )
@ UseGuards ( JwtAuthGuard , RolesGuard )
export class AdminController {
@ Get ( 'dashboard' )
@ Roles ( 'admin' )
getDashboard () {
return { message: 'Admin dashboard' };
}
}
Multiple Role Protection
Allow multiple roles to access an endpoint:
// backend/src/stats/stats.controller.ts:15
@ Get ()
@ Roles ( 'monitor' , 'admin' )
@ CacheKey ( 'dashboard_stats' )
@ CacheTTL ( 300000 ) // 5 minutes
getStats () {
return this . statsService . getDashboardStats ();
}
Both monitor and admin users can access statistics.
Controller-Level Protection
Apply guards at the controller level:
// backend/src/stats/stats.controller.ts:9
@ Controller ( 'stats' )
@ UseGuards ( JwtAuthGuard , RolesGuard )
@ UseInterceptors ( CacheInterceptor )
export class StatsController {
// All routes require authentication
}
How to Assign Roles
During Registration
Roles can be assigned during user registration:
const userData = {
... dto ,
categoria: 'usuario' , // Default category
socio: 'NoSocio' , // Default membership status
// ... other fields
};
const user = await this . usersService . create ( userData );
Updating User Roles
Update a user’s category through an admin endpoint:
@ Patch ( ':id/role' )
@ Roles ( 'admin' )
async updateUserRole (
@ Param ( 'id' ) id : number ,
@ Body () dto : { categoria: string }
) {
return this . usersService . updateCategory ( id , dto . categoria );
}
Guard Execution Order
Always apply guards in the correct order: JwtAuthGuard first, then RolesGuard.
@ UseGuards ( JwtAuthGuard , RolesGuard ) // Correct order
The JwtAuthGuard authenticates the user and attaches the user object to the request. The RolesGuard then checks the user’s category against the required roles.
Accessing User Data in Controllers
Access the authenticated user’s data in route handlers:
@ Get ( 'profile' )
@ UseGuards ( JwtAuthGuard )
getProfile (@ Req () req : Request ) {
const user = req . user as any ;
console . log ( 'User category:' , user . categoria );
console . log ( 'Membership status:' , user . socio );
return user ;
}
Best Practices
Principle of Least Privilege Assign the minimum role necessary for users to perform their tasks.
Consistent Naming Use consistent category names across your application (e.g., ‘admin’ vs ‘administrador’).
Guard Chaining Always chain JwtAuthGuard before RolesGuard to ensure proper authentication.
Audit Logging Log role changes and access attempts for security auditing.
Common Patterns
Public Endpoints
Endpoints without guards are public:
@ Get ( 'public-info' )
getPublicInfo () {
return { message: 'This is public' };
}
Authenticated Only
Require authentication without role restrictions:
@ Get ( 'profile' )
@ UseGuards ( JwtAuthGuard )
getProfile () {
return { message: 'Authenticated users only' };
}
Role-Based Access
Require specific roles:
@ Get ( 'stats' )
@ UseGuards ( JwtAuthGuard , RolesGuard )
@ Roles ( 'admin' , 'monitor' )
getStats () {
return { message: 'Admin or monitor access' };
}