Inicio > Cursos > Node.js y Express > Autenticación con JWT
Node.js y ExpressExpress.jsLección 9 de 20

Autenticación con JWT

JWT (JSON Web Token) es el estándar para autenticación stateless en APIs REST. Cuando un usuario inicia sesión, el servidor genera un token firmado con una clave secreta y se lo envía. El cliente guarda ese token y lo incluye en el header Authorization de cada petición siguiente. El servidor verifica la firma sin necesidad de consultar una base de datos.

Las contraseñas NUNCA se guardan en texto plano en una base de datos. bcryptjs las convierte en un hash unidireccional irreversible. Cuando el usuario inicia sesión, bcrypt compara la contraseña ingresada con el hash guardado. Aunque alguien robe tu base de datos, no puede recuperar las contraseñas originales.

// npm install jsonwebtoken bcryptjs
const jwt = require('jsonwebtoken');
const bcrypt = require('bcryptjs');

const SECRET = process.env.JWT_SECRET; // ¡Nunca hardcodear!
const REFRESH_SECRET = process.env.JWT_REFRESH_SECRET;

// --- REGISTRO ---
const registrar = async (req, res) => {
  const { email, password, nombre } = req.body;

  // Hashear contraseña — salt rounds 12 = seguro y razonable
  const hashedPassword = await bcrypt.hash(password, 12);

  const usuario = await db.usuario.create({
    data: { email, password: hashedPassword, nombre }
  });

  res.status(201).json({ id: usuario.id, email: usuario.email });
};

// --- LOGIN ---
const login = async (req, res) => {
  const { email, password } = req.body;

  const usuario = await db.usuario.findUnique({ where: { email } });
  if (!usuario) return res.status(401).json({ error: 'Credenciales inválidas' });

  // Comparar password con hash almacenado
  const esValido = await bcrypt.compare(password, usuario.password);
  if (!esValido) return res.status(401).json({ error: 'Credenciales inválidas' });

  // Generar tokens
  const accessToken = jwt.sign(
    { userId: usuario.id, email: usuario.email },
    SECRET,
    { expiresIn: '15m' } // access token corto
  );

  const refreshToken = jwt.sign(
    { userId: usuario.id },
    REFRESH_SECRET,
    { expiresIn: '7d' } // refresh token largo
  );

  res.json({ accessToken, refreshToken });
};

// --- MIDDLEWARE de autenticación ---
const authMiddleware = (req, res, next) => {
  const auth = req.headers.authorization;
  if (!auth?.startsWith('Bearer ')) {
    return res.status(401).json({ error: 'Token requerido' });
  }

  try {
    const token = auth.split(' ')[1];
    const payload = jwt.verify(token, SECRET);
    req.user = payload; // disponible en la ruta
    next();
  } catch (err) {
    return res.status(401).json({ error: 'Token inválido o expirado' });
  }
};

// Ruta protegida
app.get('/perfil', authMiddleware, async (req, res) => {
  const usuario = await db.usuario.findUnique({
    where: { id: req.user.userId },
    select: { id: true, email: true, nombre: true } // nunca devolver password
  });
  res.json(usuario);
});
ConsejoUsa access tokens de corta duración (15min) y refresh tokens de larga (7 días). Cuando el access token expira, el cliente usa el refresh token para obtener un nuevo access token sin pedir contraseña de nuevo.
← Routers y Arquitectura MVCValidación con Zod →

Practica esta lección

Resuelve los ejercicios del curso de Node.js y Express en la app, gratis y con corrección automática.

Empezar a practicar