permissions-contractx
v2.3.0
Published
Enterprise-grade authentication and authorization package for NestJS microservices with role-based and permission-based access control
Readme
permissions-contractx
Librería compartida de autorización para la plataforma ContractX. Provee los guards, decoradores, interfaces y constantes que los microservicios usan para verificar los JWT que emite el servicio de Auth y aplicar el modelo de permisos de dominio (ADR-004).
Auth emite y firma los tokens; cada microservicio verifica los suyos usando este paquete. Ningún servicio mantiene su propio catálogo de permisos: todos referencian el vocabulario central definido en Auth.
Instalación
npm install permissions-contractxPeer dependencies
El paquete espera que el servicio consumidor ya tenga instaladas:
@nestjs/common,@nestjs/corereflect-metadata,rxjsjsonwebtokenexpress
Qué incluye
Guards
AuthorizationGuard— guard de autorización con cuatro estados de migración por endpoint (legacy / observación / estricto / solo-permisos), eje lectura/escritura derivado del método HTTP, y soporte de modo-ver (un permiso de solo lectura bloquea escrituras). Resuelve conflictos con la regla deny-overrides (deny siempre gana).JwtAuthGuard— verificación y decodificación del JWT.
Decoradores
@RequirePermissions(...)— declara los permisos que un endpoint exige.@PermissionWrites(...)— override explícito del eje lectura/escritura (por ejemplo, unGETcon efectos o unPOSTde solo lectura).@CurrentUser()— inyecta el usuario resuelto del token.@Public()— marca un endpoint como abierto.
Interfaces
JwtPayload— contrato del token que emite Auth:sub,role[],permissions[],permissionsView[],clientId[],providerId,tenantContext('client' | 'provider' | 'system'),key_client, entre otros.PermissionMode— enumWRITE/READ, espejo del modo del par rol↔permiso en Auth.
Constantes
permission-names.constants— los 130 códigos de permiso de dominio, agrupados por microservicio, másPERMISSIONS_BY_DOMAIN,ALL_PERMISSION_CODESy el tipoPermissionCode.security.constants— constantes de roles del sistema.
Uso básico
Registrar el guard de forma global en un microservicio (NestJS):
import { AuthorizationGuard } from 'permissions-contractx';
import { APP_GUARD } from '@nestjs/core';
@Module({
providers: [
{ provide: APP_GUARD, useClass: AuthorizationGuard },
],
})
export class AppModule {}Proteger un endpoint exigiendo un permiso:
import { RequirePermissions } from 'permissions-contractx';
@Controller('contracts')
export class ContractsController {
@Post()
@RequirePermissions('contracts.create')
create() { /* ... */ }
}Override del eje lectura/escritura cuando la convención HTTP no aplica:
import { PermissionWrites } from 'permissions-contractx';
// Un POST que en realidad solo consulta:
@Post('search')
@RequirePermissions('contracts.view_list')
@PermissionWrites(false)
search() { /* ... */ }Inter-Service Authentication (HMAC)
Además del modelo de autorización basado en JWT de usuario, el paquete incluye el sistema de autenticación machine-to-machine (M2M) que usan los microservicios de ContractX para llamarse entre sí de forma segura.
Cada llamada saliente lleva headers X-Service-* con una firma HMAC-SHA256 del par apiKey + secretKey del servicio emisor. El receptor verifica la firma, el timestamp (ventana de expiración + drift de reloj) y el nonce (anti-replay). Ningún JWT de usuario interviene en estas llamadas.
Para detalles de implementación, arquitectura y deuda conocida, ver src/inter-service-auth/README.md.
Configurar el módulo
import { InterServiceAuthModule, SERVICE_NAMES } from 'permissions-contractx';
import { ConfigModule, ConfigService } from '@nestjs/config';
@Module({
imports: [
InterServiceAuthModule.forRootAsync({
imports: [ConfigModule],
useFactory: (cfg: ConfigService) => ({
services: {
[SERVICE_NAMES.AUTH]: {
serviceId: SERVICE_NAMES.AUTH,
apiKey: cfg.get('AUTH_API_KEY'),
secretKey: cfg.get('AUTH_SECRET_KEY'),
},
[SERVICE_NAMES.CONTRACTS]: {
serviceId: SERVICE_NAMES.CONTRACTS,
apiKey: cfg.get('CONTRACTS_API_KEY'),
secretKey: cfg.get('CONTRACTS_SECRET_KEY'),
},
},
signatureExpireMinutes: 5,
allowedTimeDriftSeconds: 30,
enableLogging: cfg.get('SERVICE_AUTH_LOGGING') === 'true',
}),
inject: [ConfigService],
}),
],
})
export class AppModule {}El middleware (InterServiceAuthMiddleware) debe registrarse en el módulo consumidor:
import { InterServiceAuthMiddleware } from 'permissions-contractx';
export class AppModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
consumer.apply(InterServiceAuthMiddleware).forRoutes('*');
}
}Proteger un endpoint (solo M2M)
import {
ServiceAuthGuard,
RequireServiceAuth,
RequireServicePermissions,
} from 'permissions-contractx';
@Controller('internal')
@RequireServiceAuth() // rechaza cualquier request no-M2M
@UseGuards(ServiceAuthGuard)
export class InternalController {
@Get('contracts')
@RequireServicePermissions('contracts.read')
listContracts() { /* ... */ }
}Emitir una llamada M2M saliente
constructor(private readonly authSvc: InterServiceAuthService) {}
async fetchFromContracts(schema: string) {
const headers = this.authSvc.generateAuthHeaders(SERVICE_NAMES.CONTRACTS, schema);
return this.httpService.get('/internal/contracts', { headers }).toPromise();
}Decoradores disponibles
| Decorador | Propósito |
|---|---|
| @RequireServiceAuth() | Endpoint exclusivo M2M; rechaza JWT de usuario |
| @AllowServiceAuth(false) | Bloquea M2M en un endpoint específico (gana sobre clase) |
| @RequireServicePermissions(...p) | El servicio llamante debe tener todos los permisos listados |
| @RequireServiceRoles(...r) | El servicio llamante debe tener al menos uno de los roles listados |
Modelo de cuatro estados (migración)
El AuthorizationGuard permite migrar cada endpoint de forma gradual mediante la variable de entorno de enforcement:
- legacy / solo-roles — sin gateo por permisos (estado de partida).
- observación — el gateo por permisos evalúa y registra discrepancias, pero solo el rol-gate bloquea.
- estricto — ambos gates bloquean.
- solo-permisos — endpoint graduado, gateado únicamente por permisos.
Esto permite introducir el enforcement servicio por servicio sin romper el acceso existente. El principio rector es fallar-seguro: ante cualquier conflicto, gana lo más restrictivo.
Versionado
Sigue SemVer. La serie 1.x es aditiva sobre el modelo ADR-004; los cambios que retiren la API antigua de autorización se reservan para una mayor (2.0.0).
Licencia
MIT
