Volver a la página principal
viernes 20 septiembre 2024
58

Cómo implementar una autenticación JWT en Node.js con Express

La autenticación basada en JWT (JSON Web Tokens) se ha vuelto extremadamente popular en el desarrollo de aplicaciones web modernas debido a su simplicidad y seguridad. En una arquitectura de APIs RESTful, los tokens JWT permiten manejar la autenticación sin necesidad de mantener sesiones en el servidor. Estos tokens se envían en las cabeceras de las solicitudes y contienen la información de autenticación que el servidor puede verificar y validar fácilmente.

En este artículo, te mostraremos cómo implementar autenticación JWT en una API construida con Node.js y Express. Cubriremos los aspectos clave, desde la instalación de los paquetes necesarios hasta la creación de rutas protegidas.

¿Qué es JWT?

JSON Web Tokens (JWT) es un estándar abierto que define una forma compacta y autocontenida para transmitir información de manera segura entre el cliente y el servidor como un objeto JSON. Los tokens JWT consisten en tres partes:

1. Header (Encabezado): Contiene el tipo de token y el algoritmo de cifrado utilizado.

2. Payload (Cuerpo): Contiene la información o los "claims" (reclamaciones) que se desean transmitir.

3. Signature (Firma): Asegura que el token no ha sido alterado y valida la identidad del emisor.

Un JWT típico tiene el siguiente aspecto:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImpvaG5kb2UiLCJpYXQiOjE2MjkxOTU2MDB9.LV8NOL2A3Hzl2dD3U82e3WqzjJQQQckWqxJ8YFxDYHs

Flujo de autenticación JWT

El flujo básico de autenticación con JWT sigue estos pasos:

1. Inicio de sesión: El usuario se autentica con credenciales (nombre de usuario y contraseña).

2. Generación del token: Si las credenciales son correctas, el servidor genera un JWT y lo envía al cliente.

3. Almacenamiento del token: El cliente almacena el JWT (normalmente en localStorage o cookies).

4. Autenticación en cada solicitud: Para acceder a rutas protegidas, el cliente envía el JWT en la cabecera de las solicitudes.

5. Validación del token: El servidor verifica el JWT en cada solicitud entrante para conceder o denegar el acceso.

Ahora que tenemos claro el concepto, veamos cómo implementar JWT en una API de Node.js con Express.

Requisitos previos

Antes de empezar, asegúrate de tener lo siguiente:

  • Node.js instalado en tu sistema.
  • NPM (Node Package Manager) instalado.
  • Conocimientos básicos de Express y cómo configurar una API.

Paso 1: Crear un proyecto de Node.js con Express

Primero, crea un nuevo proyecto de Node.js e instala Express:

mkdir jwt-auth-app
cd jwt-auth-app
npm init -y
npm install express jsonwebtoken bcryptjs body-parser
  • express: El framework para construir la API.
  • jsonwebtoken: Paquete para generar y verificar JWTs.
  • bcryptjs: Para encriptar y comparar contraseñas de forma segura.
  • body-parser: Para parsear los datos de las solicitudes POST.

Paso 2: Configurar Express y Middlewares

Ahora, crea el archivo principal de tu aplicación (app.js) y configura Express.

const express = require('express');
const bodyParser = require('body-parser');

const app = express();

// Middleware para parsear JSON
app.use(bodyParser.json());

const PORT = process.env.PORT || 3000;

app.listen(PORT, () => {
  console.log(`Servidor corriendo en el puerto ${PORT}`);
});

Paso 3: Simular una base de datos de usuarios

En una aplicación real, los usuarios se almacenan en una base de datos como MySQL o MongoDB. Para este ejemplo, simularemos una base de datos con un simple arreglo de usuarios.

const users = [
  {
    id: 1,
    username: 'johndoe',
    password: '$2a$10$C1ZdYOXw.7D/j0RGXE2TpefXHEdzhpQEm/6Rk89ZOCTcONKc6JIXC', // contraseña encriptada "password123"
  },
];

Usaremos la biblioteca bcryptjs para manejar las contraseñas encriptadas y evitar almacenar contraseñas en texto plano.

Paso 4: Registrar y autenticar usuarios

Vamos a crear dos rutas: una para registrar usuarios y otra para autenticar (inicio de sesión).

Registro de usuario

Para registrar un usuario, necesitamos almacenar su nombre de usuario y contraseña encriptada.

const bcrypt = require('bcryptjs');

// Ruta de registro
app.post('/register', async (req, res) => {
  const { username, password } = req.body;

  // Verificar si el usuario ya existe
  const userExists = users.find(user => user.username === username);
  if (userExists) {
    return res.status(400).json({ message: 'El usuario ya existe' });
  }

  // Encriptar la contraseña
  const salt = await bcrypt.genSalt(10);
  const hashedPassword = await bcrypt.hash(password, salt);

  // Crear el nuevo usuario
  const newUser = { id: users.length + 1, username, password: hashedPassword };
  users.push(newUser);

  res.status(201).json({ message: 'Usuario registrado exitosamente' });
});

Autenticación de usuario (login)

En la ruta de inicio de sesión, el usuario enviará su nombre de usuario y contraseña. Verificaremos las credenciales y generaremos un JWT si son correctas.

const jwt = require('jsonwebtoken');

// Clave secreta para firmar el JWT
const JWT_SECRET = 'mi_secreto_super_seguro';

// Ruta de login
app.post('/login', async (req, res) => {
  const { username, password } = req.body;

  // Verificar si el usuario existe
  const user = users.find(user => user.username === username);
  if (!user) {
    return res.status(400).json({ message: 'Credenciales inválidas' });
  }

  // Verificar la contraseña
  const isMatch = await bcrypt.compare(password, user.password);
  if (!isMatch) {
    return res.status(400).json({ message: 'Credenciales inválidas' });
  }

  // Generar el token JWT
  const token = jwt.sign({ id: user.id, username: user.username }, JWT_SECRET, {
    expiresIn: '1h',
  });

  res.json({ token });
});

Paso 5: Proteger rutas con JWT

Ahora que hemos implementado la autenticación y generación de tokens, crearemos una ruta protegida. Para acceder a esta ruta, el cliente debe proporcionar un JWT válido en la cabecera de la solicitud.

Primero, creamos un middleware que se encargue de verificar si el token es válido.

Middleware de verificación de JWT

const verifyToken = (req, res, next) => {
  const token = req.header('Authorization');

  if (!token) {
    return res.status(401).json({ message: 'Acceso denegado, token requerido' });
  }

  try {
    const verified = jwt.verify(token.split(' ')[1], JWT_SECRET); // Extraer el token después de "Bearer"
    req.user = verified;
    next();
  } catch (err) {
    res.status(400).json({ message: 'Token no válido' });
  }
};

Ruta protegida

Utilizamos el middleware verifyToken en cualquier ruta que queramos proteger.

// Ruta protegida
app.get('/dashboard', verifyToken, (req, res) => {
  res.json({ message: `Bienvenido, ${req.user.username}` });
});

En esta ruta, el token debe ser enviado en la cabecera de la solicitud como Authorization: Bearer <token>. Si el token es válido, el middleware permitirá el acceso a la ruta.

Paso 6: Probar la API

Ahora que hemos implementado todas las partes, podemos probar la API usando una herramienta como Postman o curl.

1. Registrar un usuario

  • Método: POST
  • Ruta: /register
  • Body (JSON):
{
    "username": "johndoe",
    "password": "password123"
  }

2. Iniciar sesión (autenticación)

  • Método: POST
  • Ruta: /login
  • Body (JSON):
{
    "username": "johndoe",
    "password": "password123"
  }
  • Respuesta esperada (JSON):
{
    "token": "eyJhbGciOiJIUzI1NiIsInR..."
  }

3. Acceder a una ruta protegida

  • Método: GET
  • Ruta: /dashboard
  • Headers:
Authorization: Bearer <token>
  • Respuesta esperada (JSON):
{
    "message": "Bienvenido, johndoe"


  }

Conclusión

En este artículo, hemos aprendido cómo implementar la autenticación basada en JWT en una API de Node.js con Express. Este tipo de autenticación es ideal para aplicaciones modernas que requieren una capa de seguridad ligera y escalable, sin la necesidad de mantener sesiones del lado del servidor.

JWT ofrece una solución flexible para la autenticación, permitiendo a los desarrolladores construir APIs seguras y manejables, al tiempo que proporciona una experiencia fluida para los usuarios.

Compartir:
Creado por:
Author photo

Jorge García

Fullstack developer