service-auto-register-client-nodejs
v0.1.48
Published
Librería para auto-registro de microservicios Node.js con validación JWT mediante RS256, ya se valida si el servicio es interno o externo, y si es backend o frontend. Opciones de integración con Express y Socket.IO.
Maintainers
Readme
Service Auto Register Client - Node.js
Librería para auto-registro de microservicios Node.js con validación de tokens JWT, similar a la versión Java. Proporciona:
- ✅ Auto-registro automático en el auth-service
- ✅ Validación de tokens JWT
- ✅ Protección de endpoints con roles y permisos
- ✅ Soporte para WebSockets con Socket.IO
- ✅ Gestión criptográfica de llaves
Instalación
npm install service-auto-register-client
# o
yarn add service-auto-register-clientConfiguración Rápida
1. Crear archivo .env
SERVICE_NAME=mi-servicio
APP_SERVICE_BASE_URL=http://localhost:3001
SERVICE_PORT=3001
LOG_LEVEL=info2. Inicializar en tu aplicación Express
const express = require('express');
const { initializeServiceAutoRegister, validateTokenWithOptions } = require('service-auto-register-client');
const app = express();
// Inicializar auto-registro
const registration = await initializeServiceAutoRegister({
serviceName: 'mi-servicio',
serviceBaseUrl: 'http://localhost:3001',
authServiceUrl: 'http://localhost:8082'
});
// Usar la validación de tokens (método para endpoints individuales)
app.get('/api/protected', validateTokenWithOptions(), (req, res) => {
res.json({ user: req.user });
});
app.listen(3001);Middleware de Validación Global
Si quieres proteger toda tu aplicación con validación de tokens, puedes usar el middleware de manera global:
const express = require('express');
const { validateToken } = require('service-auto-register-client');
const app = express();
// Definir rutas públicas ANTES del middleware global
app.get('/health', (req, res) => {
res.json({ status: 'OK' });
});
app.post('/api/auth/login', (req, res) => {
// Lógica de login...
res.json({ token: 'jwt-token' });
});
// Aplicar validación de tokens a todas las rutas siguientes
app.use(validateToken);
// Todas las rutas después de esta línea requerirán token válido automáticamente
app.get('/api/profile', (req, res) => {
res.json({
message: 'Endpoint protegido automáticamente',
user: req.user
});
});
app.get('/api/data', (req, res) => {
// El token ya fue validado por el middleware global
res.json({
data: 'información privada',
userId: req.user.id
});
});Ventajas del middleware global:
- Protege automáticamente todos los endpoints definidos después
- El token y user info están disponibles en
req.tokenContextyreq.user - No necesitas agregar validación a cada endpoint individualmente
- Para validaciones específicas (roles/permisos), sigue usando
validateTokenWithOptions()
Uso con Express
Endpoints sin protección
app.get('/health', (req, res) => {
res.json({ status: 'OK' });
});
app.post('/api/auth/login', (req, res) => {
// Lógica de login...
res.json({ token: 'jwt-token', expiresIn: 3600 });
});Endpoints con token válido
const { validateTokenWithOptions } = require('service-auto-register-client');
app.get('/api/profile',
validateTokenWithOptions(),
(req, res) => {
res.json({ user: req.user });
}
);Nota: Si usas el middleware global
validateToken, no necesitas agregarlo a endpoints individuales. Si necesitas validaciones específicas (roles/permisos), usavalidateTokenWithOptions()en endpoints individuales.
Endpoints que requieren roles
app.delete('/api/users/:id',
validateTokenWithOptions({ roles: ['ADMIN', 'SUPER_ADMIN'] }),
(req, res) => {
res.json({ deleted: true });
}
);Endpoints que requieren permisos
app.post('/api/reports',
validateTokenWithOptions({
permissions: ['CREATE_REPORTS'],
errorMessage: 'No tienes permiso para crear reportes'
}),
(req, res) => {
res.json({ reportId: 123 });
}
);Endpoints con validaciones complejas
app.put('/api/users/:id',
validateTokenWithOptions({
roles: ['ADMIN', 'MANAGER'],
permissions: ['WRITE_USERS'],
checkExpiration: true,
errorMessage: 'Acceso denegado'
}),
(req, res) => {
res.json({ updated: true });
}
);Uso con Socket.IO (WebSockets)
Configuración básica
const express = require('express');
const { createServer } = require('http');
const { Server } = require('socket.io');
const { initializeServiceAutoRegister } = require('service-auto-register-client');
const app = express();
const httpServer = createServer(app);
const io = new Server(httpServer, {
cors: { origin: '*' }
});
const registration = await initializeServiceAutoRegister({
serviceName: 'realtime-service',
serviceBaseUrl: 'http://localhost:3002'
});
const webSocketMiddleware = registration.webSocketMiddleware;
// Aplicar middleware de validación
io.use(webSocketMiddleware.socketIOMiddleware());
io.on('connection', (socket) => {
console.log(`Usuario conectado: ${socket.user.username}`);
socket.on('message', (data) => {
io.emit('broadcast', data);
});
});
httpServer.listen(3002);Eventos que requieren roles
socket.on('admin-action', (data) => {
try {
webSocketMiddleware.requireRole(socket, ['ADMIN']);
io.emit('admin-event', data);
} catch (error) {
socket.emit('error', { message: error.message });
}
});Eventos que requieren permisos
socket.on('manage-users', (data) => {
try {
webSocketMiddleware.requirePermission(socket, ['MANAGE_USERS']);
io.emit('user-update', data);
} catch (error) {
socket.emit('error', { message: error.message });
}
});Propiedades de Configuración
Inicialización completa
const registration = await initializeServiceAutoRegister({
// Habilitación
enabled: true,
// URLs
authServiceUrl: 'http://localhost:8082',
serviceName: 'mi-servicio',
serviceBaseUrl: 'http://localhost:3001',
servicePort: 3001,
// Llaves del servicio (se generan automáticamente si no existen)
publicKeyPath: 'keys/service_public_key.pem', // Llave pública del servicio
privateKeyPath: 'keys/service_private_key.pem', // Llave privada del servicio
// Llave del auth-service (debe copiarse manualmente)
authPublicKeyPath: 'keys/public_key.pem',
// Reintentos
maxRetryAttempts: 3,
retryDelaySeconds: 5,
// Comportamiento
failOnRegistrationError: false,
interno: false, // true si es servicio interno
backend: false, // true si es backend
// Headers adicionales
httpHeaders: {
'X-Custom-Header': 'value'
}
});🔑 Manejo Automático de Llaves
La librería gestiona automáticamente las llaves criptográficas:
- Primera ejecución: Genera un par de llaves RSA y las guarda en disco
- Ejecuciones posteriores: Reutiliza las llaves existentes
- Servicio ya registrado: Detecta HTTP 409 Conflict y no reintenta
Archivos generados:
keys/
├── service_public_key.pem # Generada automáticamente
├── service_private_key.pem # Generada automáticamente
└── public_key.pem # Debes copiarla del auth-serviceVariables de Entorno
# Servicio
SERVICE_NAME=mi-servicio
APP_SERVICE_BASE_URL=http://localhost:3001
SERVICE_PORT=3001
# Auth
AUTH_SERVICE_URL=http://localhost:8082
AUTH_PUBLIC_KEY_PATH=keys/public_key.pem
# Reintentos
MAX_RETRY_ATTEMPTS=3
RETRY_DELAY_SECONDS=5
# Logging
LOG_LEVEL=infoAccediendo a datos del usuario en endpoints
app.get('/api/my-data', validateToken(), (req, res) => {
// req.user contiene:
// - id: ID del usuario
// - username: nombre de usuario
// - email: correo electrónico
// - roles: array de roles
// - permissions: array de permisos
// req.tokenContext contiene el TokenContext completo
console.log(`Usuario: ${req.user.username}`);
console.log(`Roles: ${req.user.roles.join(', ')}`);
console.log(`Permisos: ${req.user.permissions.join(', ')}`);
res.json({ user: req.user });
});Accediendo a datos del usuario en WebSockets
socket.on('custom-event', (data) => {
// socket.user contiene la información del usuario
// socket.tokenContext contiene el TokenContext completo
console.log(`Usuario: ${socket.user.username}`);
console.log(`Roles: ${socket.user.roles.join(', ')}`);
console.log(`Email: ${socket.user.email}`);
});Cómo conectarse desde el cliente
Express (HTTP)
# Obtener token (login)
curl -X POST http://localhost:3001/api/auth/login \
-H "Content-Type: application/json" \
-d '{"username":"user","password":"pass"}'
# Usar token en requests
curl http://localhost:3001/api/protected \
-H "Authorization: Bearer TU_TOKEN_JWT"Socket.IO (WebSocket)
// Cliente JavaScript
const token = localStorage.getItem('accessToken');
const socket = io('http://localhost:3002', {
query: { token: token }
// O como header
// extraHeaders: { Authorization: `Bearer ${token}` }
});
socket.on('connect', () => {
console.log('Conectado');
});
socket.emit('message', { text: 'Hola' });Estructura del proyecto
src/
├── index.js # Punto de entrada
├── client/
│ └── ServiceAutoRegisterClient.js # Cliente de registro
├── config/
│ └── ServiceAutoRegisterConfig.js # Configuración
├── security/
│ ├── CryptoUtil.js # Utilidades criptográficas
│ ├── TokenValidationService.js # Validación de tokens
│ └── TokenContext.js # Contexto del token
├── decorators/
│ └── ValidateToken.js # Middleware de validación
├── websocket/
│ └── WebSocketTokenValidationMiddleware.js # Validación WebSocket
└── utils/
└── Logger.js # Logger centralizado
examples/
├── express-example.js # Ejemplo con Express
└── socket-io-example.js # Ejemplo con Socket.IOFlujo de funcionamiento
1. Inicializar app
↓
2. initializeServiceAutoRegister()
↓
3. Generar llaves RSA del servicio
↓
4. Firmar datos (serviceName:baseUrl)
↓
5. Enviar solicitud POST a /api/services
↓
6. Auth-service valida la firma
↓
7. Servicio registrado ✅
↓
8. Usar validateToken() en endpoints
↓
9. Cliente envía token en Authorization header
↓
10. Validar token usando llave pública
↓
11. Acceso permitido ✅Licencia
MIT
Soporte
Para reportar bugs o sugerencias: github.com/chaqui/service-auto-register-client
