Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/camiloivcode/biblioteca-la-palabra/llms.txt

Use this file to discover all available pages before exploring further.

La autenticación de La Palabra se basa en un esquema dual-token JWT: un access token de corta duración (8 horas) para autenticar cada request, y un refresh token de larga duración (7 días) para renovarlo de forma transparente sin obligar al usuario a volver a iniciar sesión. Ambos tokens se generan al hacer login y se almacenan en el localStorage del navegador. El cliente api.js gestiona el refresco automático: al recibir un 401, intenta renovar el access token antes de reintentar la petición original.
Los secretos JWT deben cambiarse obligatoriamente antes de desplegar en producción. Los valores por defecto del archivo .env.example (fallback_secret_dev y fallback_refresh_secret_dev) son públicos y no ofrecen ninguna seguridad. Genera secretos largos y aleatorios para JWT_SECRET y JWT_REFRESH_SECRET en el entorno de producción.

Tokens

Tipo de tokenVariable de entornoExpiraciónAlmacenamiento
Access tokenJWT_SECRET8hlocalStorage
Refresh tokenJWT_REFRESH_SECRET7dlocalStorage
Los valores de expiración también son configurables mediante JWT_EXPIRES_IN y JWT_REFRESH_EXPIRES_IN respectivamente. La configuración se centraliza en backend/src/config/jwt.config.js:
module.exports = {
  secret: process.env.JWT_SECRET || 'fallback_secret_dev',
  expiresIn: process.env.JWT_EXPIRES_IN || '8h',
  refreshSecret: process.env.JWT_REFRESH_SECRET || 'fallback_refresh_secret_dev',
  refreshExpiresIn: process.env.JWT_REFRESH_EXPIRES_IN || '7d',
};

Flujo de autenticación

1

Login inicial

El cliente envía POST /api/auth/login con { email, password }. Si las credenciales son válidas, la API responde con { accessToken, refreshToken, user }.
2

Almacenamiento de tokens

El frontend guarda accessToken, refreshToken y los datos de user en localStorage del navegador.
3

Requests autenticadas

Cada llamada a la API incluye el access token en el encabezado HTTP: Authorization: Bearer <accessToken>. El cliente api.js lo adjunta automáticamente leyendo localStorage.getItem('accessToken').
4

Detección de token expirado

Si el servidor responde con HTTP 401, el cliente api.js intercepta el error e intenta renovar el access token llamando a POST /api/auth/refresh con el refreshToken almacenado.
5

Renovación exitosa

Si el refresh endpoint responde con { success: true, data: { accessToken } }, el cliente reemplaza el token en localStorage y reintenta la petición original con el nuevo access token, de forma completamente transparente para el usuario.
6

Refresh fallido — cierre de sesión

Si el refresh token también ha expirado o es inválido, el cliente llama a localStorage.clear(), elimina todos los datos de sesión, y redirige al usuario a /login.

Middleware de autenticación

auth.middleware.js protege todas las rutas que requieren un usuario autenticado. Su funcionamiento es el siguiente:
  1. Lee el encabezado Authorization y extrae el token del esquema Bearer.
  2. Verifica la firma y la expiración del JWT usando jwt.verify() con el secreto configurado.
  3. Si el token es válido, busca al usuario en la base de datos para confirmar que existe y está activo.
  4. Adjunta el objeto de usuario (id, nombre, email, role, activo) en req.user para que los controllers y servicios posteriores puedan usarlo.
const authMiddleware = async (req, res, next) => {
  try {
    const authHeader = req.headers.authorization;

    if (!authHeader || !authHeader.startsWith('Bearer ')) {
      throw new AppError('Token de acceso requerido', 401);
    }

    const token = authHeader.split(' ')[1];

    let decoded;
    try {
      decoded = jwt.verify(token, jwtConfig.secret);
    } catch (err) {
      if (err.name === 'TokenExpiredError') {
        throw new AppError('Sesión expirada. Por favor, inicia sesión nuevamente', 401);
      }
      throw new AppError('Token inválido', 401);
    }

    const user = await prisma.user.findUnique({
      where: { id: decoded.id },
      select: { id: true, nombre: true, email: true, role: true, activo: true },
    });

    if (!user) throw new AppError('Usuario no encontrado', 401);
    if (!user.activo) throw new AppError('Cuenta desactivada. Contacta al administrador', 403);

    req.user = user;
    next();
  } catch (error) {
    next(error);
  }
};

Control de roles

role.middleware.js complementa a auth.middleware.js restringiendo el acceso a rutas específicas según el rol del usuario autenticado. Se usa principalmente para proteger la gestión de usuarios del sistema (/api/users), que solo puede ser administrada por un ADMIN. El middleware es una función de orden superior que recibe una lista de roles permitidos y devuelve el handler correspondiente:
const requireRole = (...roles) => {
  return (req, res, next) => {
    if (!req.user) {
      return next(new AppError('No autenticado', 401));
    }
    if (!roles.includes(req.user.role)) {
      return next(new AppError('No tienes permisos para realizar esta acción', 403));
    }
    next();
  };
};
Se encadena en la definición de rutas después de authMiddleware:
// Ejemplo de uso en routes/index.js
router.get('/users', authMiddleware, requireRole('ADMIN'), usersController.getAll);
Si el usuario autenticado no tiene el rol requerido, el middleware responde con HTTP 403 Forbidden.

Recuperación de contraseña

El sistema implementa un flujo completo de recuperación de contraseña por correo electrónico mediante Nodemailer:
1

Solicitar recuperación

El usuario envía POST /api/auth/forgot-password con su email. El backend genera un token seguro aleatorio (32 bytes hex, almacenado como hash SHA-256), lo guarda en los campos passwordResetToken y passwordResetExpires del modelo User con una expiración de 1 hora, y envía un correo con el enlace de recuperación.
2

Restablecer contraseña

El usuario hace clic en el enlace recibido por correo, que lo lleva a la página /reset-password con el token como parámetro. El frontend envía POST /api/auth/reset-password con el token y la nueva contraseña. El backend valida el token, comprueba que passwordResetExpires no haya vencido (ventana de 1 hora), y actualiza la contraseña del usuario con el hash bcrypt correspondiente.

Solicitud de registro

Dado que los usuarios del sistema (bibliotecarios) son creados por el administrador, el sistema provee un flujo de solicitud de registro en lugar de auto-registro directo:
  • POST /api/auth/register-request recibe los datos del aspirante y envía una notificación por correo electrónico al administrador con la información del solicitante.
  • No se crea ninguna cuenta en este paso. El administrador debe aprobar la solicitud y crear el usuario manualmente desde el panel de gestión de usuarios.

Build docs developers (and LLMs) love