Autenticación y Autorización
Autenticación y Autorización
Sección titulada «Autenticación y Autorización»Aprende a implementar autenticación segura y control de acceso basado en roles (RBAC) en tus aplicaciones Veloce-TS.
Tabla de Contenidos
Sección titulada «Tabla de Contenidos»- Resumen
- Configuración de Autenticación
- Configuración RBAC
- Decoradores
- Gestión de Usuarios
- Mejores Prácticas de Seguridad
- Ejemplos
Resumen
Sección titulada «Resumen»Veloce-TS v0.2.6 proporciona un sistema completo de autenticación y autorización:
- Autenticación basada en JWT con tokens de acceso y actualización
- Control de Acceso Basado en Roles (RBAC) con roles jerárquicos
- Autorización basada en Permisos para control de acceso granular
- Gestión de Usuarios con hash de contraseñas y validación
- Gestión de Sesiones (opcional)
Configuración de Autenticación
Sección titulada «Configuración de Autenticación»1. Instalar Plugin de Autenticación
Sección titulada «1. Instalar Plugin de Autenticación»import { VeloceTS } from 'veloce-ts';
const app = new VeloceTS();
app.install('auth', { jwt: { secret: process.env.JWT_SECRET || 'tu-clave-secreta', accessTokenTTL: 3600, // 1 hora refreshTokenTTL: 86400, // 24 horas }, userProvider: { findByCredentials: async (username: string, password: string) => { // Buscar usuario por nombre de usuario/email const user = await userService.findByUsername(username); if (!user) return null;
// Verificar contraseña const isValid = await userService.verifyPassword(password, user.password); return isValid ? user : null; }, findById: async (id: string) => { return await userService.findById(id); }, hashPassword: async (password: string) => { return await bcrypt.hash(password, 10); }, verifyPassword: async (password: string, hash: string) => { return await bcrypt.compare(password, hash); } }});2. Crear Controlador de Autenticación
Sección titulada «2. Crear Controlador de Autenticación»import { Controller, Post, Get, Body, Auth, CurrentUser} from 'veloce-ts';import { z } from 'zod';
const LoginSchema = z.object({ username: z.string().min(1), password: z.string().min(6)});
const RegisterSchema = z.object({ username: z.string().min(3).max(50), email: z.string().email(), password: z.string().min(6)});
@Controller('/auth')export class AuthController {
@Post('/login') async login(@Body(LoginSchema) credentials: z.infer<typeof LoginSchema>) { const user = await userService.validateCredentials( credentials.username, credentials.password );
if (!user) { throw new UnauthorizedException('Credenciales inválidas'); }
const tokens = authService.generateTokens({ sub: user.id, username: user.username, email: user.email, role: user.role, permissions: user.permissions });
return { accessToken: tokens.accessToken, refreshToken: tokens.refreshToken, user: { id: user.id, username: user.username, email: user.email, role: user.role } }; }
@Post('/register') async register(@Body(RegisterSchema) userData: z.infer<typeof RegisterSchema>) { const existingUser = await userService.findByUsername(userData.username); if (existingUser) { throw new ConflictException('El nombre de usuario ya existe'); }
const hashedPassword = await userService.hashPassword(userData.password); const user = await userService.create({ ...userData, password: hashedPassword, role: 'viewer', // Rol por defecto permissions: ['tasks.read'] // Permisos por defecto });
return { message: 'Usuario creado exitosamente', user: { id: user.id, username: user.username, email: user.email, role: user.role } }; }
@Post('/refresh') async refresh(@Body() body: { refreshToken: string }) { try { const tokens = await authService.refreshToken(body.refreshToken); return tokens; } catch (error) { throw new UnauthorizedException('Token de actualización inválido'); } }
@Get('/me') @Auth() async getProfile(@CurrentUser() user: any) { return { id: user.id, username: user.username, email: user.email, role: user.role, permissions: user.permissions }; }
@Post('/logout') @Auth() async logout(@CurrentUser() user: any) { // Invalidar token de actualización await authService.invalidateRefreshToken(user.id); return { message: 'Sesión cerrada exitosamente' }; }}Configuración RBAC
Sección titulada «Configuración RBAC»1. Instalar Plugin RBAC
Sección titulada «1. Instalar Plugin RBAC»app.install('rbac', { roles: [ { name: 'admin', level: 100, description: 'Acceso completo al sistema' }, { name: 'manager', level: 80, description: 'Acceso de gestión de equipos' }, { name: 'team_lead', level: 60, description: 'Acceso de líder de equipo' }, { name: 'developer', level: 40, description: 'Acceso de desarrollador' }, { name: 'viewer', level: 20, description: 'Acceso de solo lectura' } ], permissions: [ // Permisos de usuarios 'users.create', 'users.read', 'users.update', 'users.delete', 'users.list', 'users.change_role',
// Permisos de tareas 'tasks.create', 'tasks.read', 'tasks.update', 'tasks.delete', 'tasks.assign', 'tasks.complete',
// Permisos de equipos 'teams.create', 'teams.read', 'teams.update', 'teams.delete', 'teams.add_member', 'teams.remove_member',
// Permisos de administrador 'admin.users', 'admin.tasks', 'admin.teams', 'admin.analytics' ]});2. Jerarquía de Roles
Sección titulada «2. Jerarquía de Roles»Los roles son jerárquicos basados en su nivel:
- Admin (100): Acceso completo a todo
- Manager (80): Puede gestionar equipos y usuarios
- Team Lead (60): Puede gestionar tareas del equipo
- Developer (40): Puede crear y actualizar tareas
- Viewer (20): Acceso de solo lectura
Decoradores
Sección titulada «Decoradores»Decoradores de Autenticación
Sección titulada «Decoradores de Autenticación»// Requerir autenticación@Get('/protected')@Auth()async protectedRoute(@CurrentUser() user: any) { return { message: 'Esta ruta está protegida', user: user.username };}Decoradores Basados en Roles
Sección titulada «Decoradores Basados en Roles»// Requerir nivel mínimo de rol@Get('/admin-only')@MinimumRole('admin')async adminOnly() { return { message: 'Acceso de administrador concedido' };}
@Get('/manager-up')@MinimumRole('manager')async managerUp() { return { message: 'Acceso de nivel manager concedido' };}Decoradores Basados en Permisos
Sección titulada «Decoradores Basados en Permisos»// Permiso único@Post('/users')@Permissions('users.create')async createUser(@Body() userData: any) { return await userService.create(userData);}
// Múltiples permisos (el usuario necesita TODOS)@Put('/users/:id')@Permissions('users.update', 'users.read')async updateUser(@Param('id') id: string, @Body() userData: any) { return await userService.update(id, userData);}
// Verificaciones complejas de permisos@Delete('/users/:id')@Permissions('users.delete')@MinimumRole('manager') // También requerir rol managerasync deleteUser(@Param('id') id: string) { return await userService.delete(id);}Gestión de Usuarios
Sección titulada «Gestión de Usuarios»Ejemplo de Servicio de Usuario
Sección titulada «Ejemplo de Servicio de Usuario»import { Injectable } from 'veloce-ts';import bcrypt from 'bcrypt';
@Injectable('singleton')export class UserService {
async findByUsername(username: string): Promise<User | null> { // Tu lógica de consulta a base de datos return await this.db.query('SELECT * FROM users WHERE username = ?', [username]); }
async findById(id: string): Promise<User | null> { return await this.db.query('SELECT * FROM users WHERE id = ?', [id]); }
async validateCredentials(username: string, password: string): Promise<User | null> { const user = await this.findByUsername(username); if (!user) return null;
const isValid = await this.verifyPassword(password, user.password); return isValid ? user : null; }
async hashPassword(password: string): Promise<string> { return await bcrypt.hash(password, 10); }
async verifyPassword(password: string, hash: string): Promise<boolean> { return await bcrypt.compare(password, hash); }
async create(userData: CreateUserDto): Promise<User> { const hashedPassword = await this.hashPassword(userData.password);
const user = { ...userData, password: hashedPassword, id: generateId(), createdAt: new Date(), updatedAt: new Date() };
await this.db.query( 'INSERT INTO users (id, username, email, password, role, permissions) VALUES (?, ?, ?, ?, ?, ?)', [user.id, user.username, user.email, user.password, user.role, JSON.stringify(user.permissions)] );
return user; }}Mejores Prácticas de Seguridad
Sección titulada «Mejores Prácticas de Seguridad»1. Variables de Entorno
Sección titulada «1. Variables de Entorno»JWT_SECRET=tu-clave-secreta-jwt-super-segura-aquiJWT_ACCESS_TTL=3600JWT_REFRESH_TTL=86400BCRYPT_ROUNDS=122. Seguridad de Contraseñas
Sección titulada «2. Seguridad de Contraseñas»// Usar requisitos de contraseña fuertesconst PasswordSchema = z.string() .min(8, 'La contraseña debe tener al menos 8 caracteres') .regex(/[A-Z]/, 'La contraseña debe contener letra mayúscula') .regex(/[a-z]/, 'La contraseña debe contener letra minúscula') .regex(/[0-9]/, 'La contraseña debe contener número') .regex(/[^A-Za-z0-9]/, 'La contraseña debe contener carácter especial');3. Gestión de Tokens
Sección titulada «3. Gestión de Tokens»// Implementar lista negra de tokens@Injectable('singleton')export class TokenService { private blacklistedTokens = new Set<string>();
blacklistToken(tokenId: string) { this.blacklistedTokens.add(tokenId); }
isTokenBlacklisted(tokenId: string): boolean { return this.blacklistedTokens.has(tokenId); }}4. Limitación de Velocidad
Sección titulada «4. Limitación de Velocidad»import { rateLimit } from 'veloce-ts/middleware';
// Aplicar limitación de velocidad a endpoints de autenticaciónapp.use('/auth/login', rateLimit({ windowMs: 15 * 60 * 1000, // 15 minutos max: 5 // 5 intentos por ventana}));Ejemplos
Sección titulada «Ejemplos»Flujo Completo de Autenticación
Sección titulada «Flujo Completo de Autenticación»// 1. Registrar usuarioPOST /auth/register{ "username": "johndoe", "email": "john@ejemplo.com", "password": "SecurePass123!"}
// 2. Iniciar sesiónPOST /auth/login{ "username": "johndoe", "password": "SecurePass123!"}
// Respuesta:{ "accessToken": "eyJhbGciOiJIUzI1NiIs...", "refreshToken": "eyJhbGciOiJIUzI1NiIs...", "user": { "id": "123", "username": "johndoe", "email": "john@ejemplo.com", "role": "viewer" }}
// 3. Usar endpoint protegidoGET /auth/meAuthorization: Bearer eyJhbGciOiJIUzI1NiIs...
// 4. Usar endpoint protegido por rolGET /admin/dashboardAuthorization: Bearer eyJhbGciOiJIUzI1NiIs...Dashboard Basado en Roles
Sección titulada «Dashboard Basado en Roles»@Controller('/dashboard')export class DashboardController {
@Get('/') @Auth() async getDashboard(@CurrentUser() user: any) { const baseDashboard = { user: { id: user.id, username: user.username, role: user.role } };
// Añadir datos específicos por rol if (user.role === 'admin') { return { ...baseDashboard, adminStats: await this.getAdminStats(), systemHealth: await this.getSystemHealth() }; }
if (user.role === 'manager') { return { ...baseDashboard, teamStats: await this.getTeamStats(user.id), pendingApprovals: await this.getPendingApprovals(user.id) }; }
return { ...baseDashboard, myTasks: await this.getUserTasks(user.id), notifications: await this.getUserNotifications(user.id) }; }
@Get('/admin-stats') @MinimumRole('admin') async getAdminStats() { return { totalUsers: await this.userService.count(), totalTasks: await this.taskService.count(), systemUptime: process.uptime() }; }}Próximos Pasos
Sección titulada «Próximos Pasos»- Ejemplo TaskMaster - Ver autenticación en acción
- Guía RBAC - Gestión avanzada de roles
- Guía de Seguridad - Mejores prácticas de seguridad
- Guía de Middleware - Middleware personalizado de autenticación