Skip to content

Autorización

Una vez que un usuario está autenticado, la autorización decide qué puede hacer. crs-backend usa control de acceso por roles, donde el rol efectivo se deriva de si el usuario es empleado o cliente.

La entidad central es User. Cada usuario tiene un Role básico y es cliente o empleado (no ambos):

role_id

client_id

employee_id

User

Role · CLIENT / EMPLOYEE

Client

ClientType · NORMAL, MARKETPLACE, RESELLER

Employee

EmployeeType · ADMIN, SALES, OPS_USA, ...

role_id

client_id

employee_id

User

Role · CLIENT / EMPLOYEE

Client

ClientType · NORMAL, MARKETPLACE, RESELLER

Employee

EmployeeType · ADMIN, SALES, OPS_USA, ...

Hay dos niveles:

  • Role — nivel de acceso básico: CLIENT o EMPLOYEE. Identifica si el usuario es cliente o personal interno.
  • ClientType / EmployeeType — el tipo fino, cuyo code es lo que se usa para autorizar.

El rol contra el que se autoriza se toma del tipo de empleado y, si no hay, del tipo de cliente:

const role =
user?.employee?.employeeType.code ?? user?.client.clientType.code;

Ese mismo code viaja en el claim role del JWT.

El enum ValidRoles define los códigos válidos. El valor del enum es el code guardado en BD y contra el que se compara.

src/auth/interfaces/roles/valid-roles.interface.ts
export enum ValidRoles {
// Tipos de cliente (client_types.code)
Normal = 'NORMAL',
Marketplace = 'MARKETPLACE',
Reseller = 'RESELLER',
// Tipos de empleado (employee_types.code)
VendedorCRS = 'SALES',
TesoreriaCRS = 'TREASURY',
OperacionesUSA = 'OPS_USA',
OperacionesLima = 'OPS_LIMA',
Administrador = 'ADMIN',
SuperAdministrador = 'SUPER_ADMIN',
ShadowRoot = 'SHADOW_ROOT',
}
ValidRolescodeAcceso
NormalNORMALCliente regular del sistema.
MarketplaceMARKETPLACEIntegración externa vía webhook/API (CompraFácil, Falabella).
ResellerRESELLERSub-courier con clientes propios; crea paquetes manualmente.
ValidRolescodeAcceso
AdministradorADMINAcceso total, gestión de usuarios y configuración.
VendedorCRSSALESGestión de clientes, asignación de tarifas.
TesoreriaCRSTREASURYPagos, vouchers, facturación.
OperacionesUSAOPS_USARecepción de paquetes en USA, creación de AWBs.
OperacionesLimaOPS_LIMAGestión de paquetes en Lima, entregas.
SuperAdministradorSUPER_ADMINPor encima del admin normal.
ShadowRootSHADOW_ROOTSuper admin total.

El decorador @Auth(...roles) combina autenticación (Passport JWT) y autorización por roles:

src/auth/decorators/auth.decorator.ts
export const Auth = (...args: ValidRoles[]) => {
return applyDecorators(
RoleProtected(...args), // guarda los roles como metadata
UseGuards(AuthGuard(), UserRoleGuard), // 1) valida JWT 2) valida roles
);
};

Uso:

// Cualquier usuario autenticado (sin restricción de rol):
@Auth()
@Get('me')
getMe(@GetUser() user: User) { ... }
// Requiere autenticación + uno de los roles indicados:
@Auth(ValidRoles.Administrador, ValidRoles.SuperAdministrador)
@Get('admins')
listAdmins() { ... }

@Auth(...roles) aplica dos cosas:

  1. RoleProtected(...roles) → guarda los roles permitidos como metadata (SetMetadata('roles', ...)).
  2. UseGuards(AuthGuard(), UserRoleGuard) → primero valida el JWT, luego los roles.

Tras validar el token, este guard decide el acceso:

token inválido

token válido

no

employeeType.code ∈ roles

empleado sin match

no es empleado

clientType.code ∈ roles

cliente sin match

Request con Bearer token

AuthGuard JWT

401 Unauthorized

JwtStrategy carga User en request.user

¿hay roles requeridos?

✓ Acceso permitido

¿es empleado?

403 Forbidden

¿es cliente?

token inválido

token válido

no

employeeType.code ∈ roles

empleado sin match

no es empleado

clientType.code ∈ roles

cliente sin match

Request con Bearer token

AuthGuard JWT

401 Unauthorized

JwtStrategy carga User en request.user

¿hay roles requeridos?

✓ Acceso permitido

¿es empleado?

403 Forbidden

¿es cliente?

En palabras:

  1. Si el endpoint no exige roles (@Auth() sin argumentos) → basta estar autenticado.
  2. Si el usuario es empleado, se valida contra employee.employeeType.code.
  3. Si no, si es cliente, se valida contra client.clientType.code.
  4. Si ninguno coincide → 403 Forbidden.

El empleado se evalúa primero: si un usuario tiene registro de empleado, su acceso se decide por el tipo de empleado, no por el de cliente.