Guía Completa: Autenticación JWT con Refresh Tokens en Node.js

Guía Completa: Autenticación JWT con Refresh Tokens en Node.js

Guía Completa: Autenticación JWT con Refresh Tokens en Node.js

La autenticación es una pieza fundamental en casi cualquier aplicación web. Aunque usar JSON Web Tokens (JWT) es una práctica común y muy extendida, podemos añadir una capa extra de seguridad y mejorar la experiencia de usuario implementando Refresh Tokens (Tokens de Actualización).

En esta guía, te explicaremos de forma detallada cómo implementar un sistema de autenticación robusto utilizando JWT junto con Refresh Tokens en un servidor de Node.js.

¿Qué es un JWT (JSON Web Token)?

Un JWT es un estándar abierto (RFC 7519) que define una forma compacta y autónoma para transmitir información de forma segura entre dos partes como un objeto JSON. Esta información puede ser verificada y confiada porque está firmada digitalmente.

Los JWT son ampliamente utilizados en aplicaciones web modernas para transmitir información de manera segura, como los datos de un usuario autenticado, evitando la necesidad de consultar la base de datos en cada petición.

Lightbox

¿Y por qué necesitamos un Refresh Token?

Un Access Token (Token de Acceso), que es el JWT que normalmente usamos, tiene una vida útil muy corta (por ejemplo, 10-15 minutos) por razones de seguridad. Si un atacante lograra robarlo, solo podría usarlo durante ese breve periodo.

Aquí es donde entra el Refresh Token. Es un token especial con una vida útil mucho más larga (días o incluso meses) cuyo único propósito es solicitar un nuevo Access Token cuando el actual ha expirado.

Esto nos permite mantener la sesión de un usuario activa sin obligarle a iniciar sesión cada 15 minutos, ofreciendo una experiencia de usuario fluida y manteniendo un alto nivel de seguridad.

Persistencia de la Sesión

Lightbox

Con este enfoque, podemos mantener la sesión de un usuario activa incluso si refresca la página o cierra el navegador. Crearemos una ruta específica en nuestra API (por ejemplo, /refresh) que se encargará de recibir el Refresh Token y devolver un nuevo Access Token.

Manos a la Obra: Implementación en Node.js

Vamos a construir nuestra API con Express.js.

Paso 1: Inicializar el Proyecto

Abre tu terminal y ejecuta los siguientes comandos para crear un nuevo proyecto de Node.js. Asegúrate de tener Node.js y npm instalados.

				
					mkdir jwt-refresh-token-auth
cd jwt-refresh-token-auth
npm init -y
				
			

Paso 2: Instalar las Dependencias Necesarias

Necesitaremos algunos paquetes para nuestro proyecto.

				
					npm install express cookie-parser dotenv jsonwebtoken body-parser
				
			
  • express: El framework para construir nuestra API.

  • cookie-parser: Middleware para gestionar las cookies de las peticiones.

  • dotenv: Para cargar variables de entorno desde un archivo .env.

  • jsonwebtoken: Para crear y verificar los JWT.

  • body-parser: Middleware para procesar el cuerpo de las peticiones (req.body).

Después de la instalación, tu archivo package.json debería lucir así:

				
					"dependencies": {
  "body-parser": "^1.20.2",
  "cookie-parser": "^1.4.6",
  "dotenv": "^16.3.1",
  "express": "^4.18.2",
  "jsonwebtoken": "^9.0.2"
}
				
			

Paso 3: Estructura del Proyecto y Archivo .env

Crea un archivo index.js en la raíz de tu proyecto. Este será nuestro archivo principal.

Además, crea un archivo .env para almacenar nuestras credenciales sensibles. Nunca subas este archivo a un repositorio público.

archivo .env:

				
					# Puerto para el servidor
PORT=8000

# Claves secretas para firmar los tokens. ¡Usa valores más seguros en producción!
ACCESS_TOKEN_SECRET=ESTAESMICLAVESECRETAPARAELACCESSTOKEN
REFRESH_TOKEN_SECRET=ESTAESMICLAVESECRETAPARAELREFRESHTOKEN
				
			

Paso 4: El Código de Nuestro Servidor (index.js)

Ahora, vamos a escribir la lógica de autenticación en nuestro archivo index.js. Crearemos un servidor Express con dos rutas principales: /login y /refresh.

  • La ruta /login validará las credenciales del usuario y, si son correctas, devolverá un Access Token y un Refresh Token. El Refresh Token se guardará en una cookie httpOnly por seguridad.

  • La ruta /refresh recibirá el Refresh Token desde la cookie y lo verificará para generar un nuevo Access Token.

archivo index.js:

				
					const express = require('express');
const cookieparser = require('cookie-parser');
const jwt = require('jsonwebtoken');
const dotenv = require('dotenv');
const bodyParser = require('body-parser');

// Cargamos las variables de entorno
dotenv.config();

const app = express();

// Middlewares para parsear JSON, datos de formularios y cookies
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(bodyParser.json());
app.use(cookieparser());

// Simulación de un usuario en una base de datos
const userCredentials = {
    username: 'admin',
    password: 'admin123',
    email: 'admin@geeksterra.com'
};

// --- RUTA DE LOGIN ---
app.post('/login', (req, res) => {
    // Extraemos el usuario y la contraseña del cuerpo de la petición
    const { username, password } = req.body;

    // Verificamos si las credenciales son correctas
    if (username === userCredentials.username && password === userCredentials.password) {
        // Creamos el Access Token (corta duración)
        const accessToken = jwt.sign({
            username: userCredentials.username,
            email: userCredentials.email
        }, process.env.ACCESS_TOKEN_SECRET, { expiresIn: '10m' });

        // Creamos el Refresh Token (larga duración)
        const refreshToken = jwt.sign({
            username: userCredentials.username,
        }, process.env.REFRESH_TOKEN_SECRET, { expiresIn: '1d' });

        // Guardamos el Refresh Token en una cookie HttpOnly
        // Es más seguro que el almacenamiento local (localStorage)
        res.cookie('jwt', refreshToken, {
            httpOnly: true, // La cookie no es accesible desde JavaScript en el cliente
            secure: true, // En producción, enviar solo sobre HTTPS
            sameSite: 'None', // Necesario para peticiones cross-site
            maxAge: 24 * 60 * 60 * 1000 // 1 día de duración
        });
        
        // Enviamos el Access Token al cliente
        return res.json({ accessToken });
    } else {
        // Si las credenciales son inválidas
        return res.status(401).json({ message: 'Credenciales inválidas' });
    }
});

// --- RUTA PARA ACTUALIZAR EL TOKEN ---
app.post('/refresh', (req, res) => {
    // Verificamos si la cookie con el Refresh Token existe
    if (req.cookies?.jwt) {
        const refreshToken = req.cookies.jwt;

        // Verificamos el Refresh Token
        jwt.verify(refreshToken, process.env.REFRESH_TOKEN_SECRET, (err, decoded) => {
            if (err) {
                // Token no válido o expirado
                return res.status(403).json({ message: 'No autorizado. Token inválido.' });
            } else {
                // Si el token es correcto, creamos un nuevo Access Token
                const accessToken = jwt.sign({
                    username: userCredentials.username,
                    email: userCredentials.email
                }, process.env.ACCESS_TOKEN_SECRET, { expiresIn: '10m' });
                
                return res.json({ accessToken });
            }
        });
    } else {
        return res.status(401).json({ message: 'No autorizado. No hay token.' });
    }
});

// Ruta de prueba del servidor
app.get('/', (req, res) => {
    res.send("¡El servidor está funcionando!");
});

const PORT = process.env.PORT || 8000;
app.listen(PORT, () => {
    console.log(`Servidor activo en http://localhost:${PORT}`);
});
				
			

¿Por qué es Mejor este Enfoque?

  • Seguridad Mejorada: Los Access Tokens tienen una vida muy corta. Si uno es robado, el daño es limitado. El Refresh Token, aunque de larga duración, solo puede ser usado para obtener un nuevo Access Token desde una ruta específica y está protegido en una cookie httpOnly.

  • Excelente Experiencia de Usuario: Los usuarios no necesitan volver a iniciar sesión constantemente. La renovación del token es transparente para ellos.

  • Sesiones Persistentes: Permite mantener sesiones de usuario a largo plazo de forma segura.

Conclusión

Implementar un sistema de autenticación con JWT y Refresh Tokens es un estándar de la industria para construir aplicaciones web seguras y eficientes. Al almacenar los Refresh Tokens en cookies HttpOnly, prevenimos ataques comunes como el XSS (Cross-Site Scripting) y aseguramos que las sesiones de nuestros usuarios sean tanto duraderas como seguras.

Este enfoque te proporciona una base sólida para gestionar la autenticación en tus proyectos de Node.js, equilibrando perfectamente la seguridad con una gran experiencia de usuario.

Link de referencia: JWT Authentication With Refresh Tokens

Facebook
X
LinkedIn
Reddit
Pinterest
Threads

Post relacionados

Post recientes

Search