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.
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
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.
Antes de empezar, asegúrate de tener lo siguiente:
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.
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}`);
});
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.
Vamos a crear dos rutas: una para registrar usuarios y otra para autenticar (inicio de sesión).
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' });
});
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 });
});
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.
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' });
}
};
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.
Ahora que hemos implementado todas las partes, podemos probar la API usando una herramienta como Postman o curl.
POST
/register
{
"username": "johndoe",
"password": "password123"
}
POST
/login
{
"username": "johndoe",
"password": "password123"
}
{
"token": "eyJhbGciOiJIUzI1NiIsInR..."
}
GET
/dashboard
Authorization: Bearer <token>
{
"message": "Bienvenido, johndoe"
}
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.
Jorge García
Fullstack developer