- Updated the backend to support token-based authentication, replacing basic auth. - Added session management functions for creating, invalidating, and refreshing sessions. - Refactored user routes to include login and logout endpoints. - Modified frontend to handle token storage and session validation. - Improved user experience by ensuring sessions are invalidated upon password changes.
137 lines
3.8 KiB
JavaScript
137 lines
3.8 KiB
JavaScript
import crypto from 'crypto';
|
|
import { getRedisClient } from '../services/redis.js';
|
|
|
|
// Duración de la sesión en segundos (24 horas)
|
|
const SESSION_DURATION = 24 * 60 * 60;
|
|
|
|
// Generar token seguro
|
|
function generateToken() {
|
|
return crypto.randomBytes(32).toString('hex');
|
|
}
|
|
|
|
// Autenticación por token Middleware
|
|
export async function authMiddleware(req, res, next) {
|
|
const redisClient = getRedisClient();
|
|
|
|
if (!redisClient) {
|
|
return res.status(500).json({ error: 'Redis no está disponible. La autenticación requiere Redis.' });
|
|
}
|
|
|
|
const authHeader = req.headers.authorization;
|
|
|
|
if (!authHeader) {
|
|
return res.status(401).json({ error: 'Authentication required', message: 'Se requiere autenticación para esta operación' });
|
|
}
|
|
|
|
const [scheme, token] = authHeader.split(' ');
|
|
|
|
if (scheme !== 'Bearer' || !token) {
|
|
return res.status(400).json({ error: 'Bad request', message: 'Se requiere un token Bearer' });
|
|
}
|
|
|
|
try {
|
|
// Verificar token en Redis
|
|
const sessionKey = `session:${token}`;
|
|
const sessionData = await redisClient.get(sessionKey);
|
|
|
|
if (!sessionData) {
|
|
return res.status(401).json({ error: 'Invalid token', message: 'Token inválido o sesión expirada' });
|
|
}
|
|
|
|
// Parsear datos de sesión
|
|
const session = JSON.parse(sessionData);
|
|
|
|
// Verificar que el usuario aún existe
|
|
const userKey = `user:${session.username}`;
|
|
const userExists = await redisClient.exists(userKey);
|
|
|
|
if (!userExists) {
|
|
// Eliminar sesión si el usuario ya no existe
|
|
await redisClient.del(sessionKey);
|
|
return res.status(401).json({ error: 'Invalid token', message: 'Usuario no encontrado' });
|
|
}
|
|
|
|
// Actualizar TTL de la sesión (refresh)
|
|
await redisClient.expire(sessionKey, SESSION_DURATION);
|
|
|
|
// Autenticación exitosa
|
|
req.user = { username: session.username };
|
|
req.token = token;
|
|
next();
|
|
} catch (error) {
|
|
console.error('Error en autenticación:', error);
|
|
res.status(500).json({ error: 'Internal server error', message: 'Error procesando autenticación' });
|
|
}
|
|
}
|
|
|
|
// Alias para mantener compatibilidad
|
|
export const basicAuthMiddleware = authMiddleware;
|
|
|
|
// Función para crear sesión
|
|
export async function createSession(username) {
|
|
const redisClient = getRedisClient();
|
|
if (!redisClient) {
|
|
throw new Error('Redis no está disponible');
|
|
}
|
|
|
|
const token = generateToken();
|
|
const sessionKey = `session:${token}`;
|
|
const sessionData = {
|
|
username,
|
|
createdAt: new Date().toISOString(),
|
|
};
|
|
|
|
// Almacenar sesión en Redis con TTL
|
|
await redisClient.setEx(sessionKey, SESSION_DURATION, JSON.stringify(sessionData));
|
|
|
|
return token;
|
|
}
|
|
|
|
// Función para invalidar sesión
|
|
export async function invalidateSession(token) {
|
|
const redisClient = getRedisClient();
|
|
if (!redisClient) {
|
|
return false;
|
|
}
|
|
|
|
try {
|
|
const sessionKey = `session:${token}`;
|
|
await redisClient.del(sessionKey);
|
|
return true;
|
|
} catch (error) {
|
|
console.error('Error invalidando sesión:', error);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Función para invalidar todas las sesiones de un usuario
|
|
export async function invalidateUserSessions(username) {
|
|
const redisClient = getRedisClient();
|
|
if (!redisClient) {
|
|
return false;
|
|
}
|
|
|
|
try {
|
|
// Buscar todas las sesiones del usuario
|
|
const keys = await redisClient.keys('session:*');
|
|
let count = 0;
|
|
|
|
for (const key of keys) {
|
|
const sessionData = await redisClient.get(key);
|
|
if (sessionData) {
|
|
const session = JSON.parse(sessionData);
|
|
if (session.username === username) {
|
|
await redisClient.del(key);
|
|
count++;
|
|
}
|
|
}
|
|
}
|
|
|
|
return count;
|
|
} catch (error) {
|
|
console.error('Error invalidando sesiones del usuario:', error);
|
|
return false;
|
|
}
|
|
}
|
|
|