
Autenticación y Seguridad: JWT vs. Server-Side Sessions en el ecosistema Next.js + NestJS
Si hay un tema en la ingeniería de software capaz de generar más discusiones que la elección entre espacios y tabs, es la autenticación.
Durante años, la comunidad frontend adoptó un estándar casi unánime: el usuario inicia sesión, la API devuelve un JSON Web Token (JWT), el frontend lo guarda en el localStorage y lo envía en el header de las siguientes peticiones. Simple, stateless (sin estado) y extremadamente peligroso.
Con la madurez del ecosistema y el auge de los frameworks full-stack como Next.js trabajando en conjunto con backends robustos como NestJS, la forma en que diseñamos la seguridad ha cambiado. Next.js ya no es solo un generador de HTML; ha asumido el papel de BFF (Backend-For-Frontend).
En este artículo, vamos a analizar a fondo los enfoques de JWT y Server-Side Sessions, los riesgos ocultos de cada uno, y cómo diseñar una arquitectura de autenticación a nivel de producción uniendo Next.js y NestJS.
El Pecado Original: JWT en el LocalStorage
Hablemos del elefante en la habitación. Guardar tu JWT de acceso en el localStorage o sessionStorage es exponer tu aplicación a ataques de XSS (Cross-Site Scripting).
Si un solo script malicioso es inyectado en tu frontend (ya sea por una dependencia comprometida en NPM, un fallo de sanitización en React o un script de analytics de terceros), ese script tiene acceso total al localStorage. Puede robar el token de tu usuario y hacerse pasar por él hasta que el token expire.
La regla de oro moderna es: El código JavaScript del cliente (el navegador) nunca debe tener acceso directo para leer el token de autenticación.
El Enfoque Estándar: JWT en Cookies HttpOnly
Para resolver el problema del XSS, el enfoque más adoptado hoy en día es almacenar el JWT en una cookie con las flags HttpOnly, Secure y SameSite=Strict.
La flag HttpOnly garantiza que el document.cookie de JavaScript no pueda leer el valor. El token viaja automáticamente en las peticiones HTTP al mismo dominio.
El Flujo Next.js + NestJS
En este escenario, Next.js actúa como un intermediario seguro:
- El cliente envía correo y contraseña a una Server Action (o Route Handler) en Next.js.
- Next.js reenvía estas credenciales a NestJS.
- NestJS las valida en la base de datos y devuelve un JWT firmado a Next.js.
- Next.js toma ese JWT y configura una cookie
HttpOnlyen el navegador del usuario.
// Next.js (Server Action de Login)
import { cookies } from 'next/headers';
export async function loginAction(formData: FormData) {
const response = await fetch('[https://api.tudominio.com/auth/login](https://api.tudominio.com/auth/login)', {
method: 'POST',
body: JSON.stringify(Object.fromEntries(formData)),
});
const { access_token } = await response.json();
// El JavaScript del navegador NUNCA verá este token
cookies().set('session_token', access_token, {
httpOnly: true,
secure: process.env.NODE_ENV === 'production',
sameSite: 'lax', // o 'strict' dependiendo de tu arquitectura de subdominios
maxAge: 60 * 60 * 24 * 7, // 1 semana
});
}
En las siguientes peticiones a la API de NestJS, si Next.js y NestJS comparten el mismo dominio raíz (ej: app.tudominio.com y api.tudominio.com), la cookie viaja automáticamente. De lo contrario, Next.js (en el servidor) lee la cookie e inyecta el token en el encabezado Authorization: Bearer antes de llamar a NestJS.
El Problema del JWT: La Invalidación
El JWT es increíble porque es stateless. NestJS no necesita ir a la base de datos para saber si el usuario ha iniciado sesión; solo verifica la firma criptográfica usando @nestjs/passport y un JwtAuthGuard. Esto escala maravillosamente bien.
Pero esta es también su mayor debilidad. ¿Cómo cierras la sesión de un usuario inmediatamente? Si la cuenta de un usuario es comprometida, o si un administrador suspende a ese usuario, el JWT seguirá siendo válido hasta que expire. No puedes "borrar" un JWT que ya fue emitido.
Si empiezas a guardar una lista negra (blacklist) de tokens revocados en Redis para comprobarla en cada petición, felicidades: acabas de convertir tu arquitectura stateless en stateful, perdiendo la principal ventaja del JWT.
Server-Side Sessions: El Retorno del Rey
Es aquí donde las Sesiones del lado del servidor (Server-Side Sessions) vuelven a brillar, especialmente en productos financieros, SaaS B2B o aplicaciones donde el control de acceso debe ser absoluto e inmediato.
En lugar de colocar un JSON firmado con los datos del usuario en el navegador, la API (NestJS) crea un registro de sesión en una base de datos rápida (como Redis) y devuelve solo un identificador opaco (un Session ID aleatorio, como un string UUID).
El Flujo con Sesión (Redis)
- Next.js envía credenciales a NestJS.
- NestJS valida, genera un
sessionId = 'abc-123', lo guarda en Redis (key: abc-123,value: { userId: 1, role: 'admin' }) y devuelve el ID. - Next.js guarda el
sessionIden una cookieHttpOnly.
Para proteger rutas en NestJS, en lugar de validar una firma criptográfica, el Guard busca la sesión en Redis:
// NestJS (Ejemplo de Guard para Sesiones con Redis)
import { Injectable, CanActivate, ExecutionContext, UnauthorizedException } from '@nestjs/common';
import { RedisService } from './redis.service';
@Injectable()
export class SessionGuard implements CanActivate {
constructor(private redis: RedisService) {}
async canActivate(context: ExecutionContext): Promise<boolean> {
const request = context.switchToHttp().getRequest();
// Next.js (BFF) extrajo la cookie y la envió vía header a la API
const sessionId = request.headers['x-session-id'];
if (!sessionId) throw new UnauthorizedException();
const sessionData = await this.redis.get(`session:${sessionId}`);
if (!sessionData) throw new UnauthorizedException();
// Inyecta el usuario en el request para que lo usen los controllers
request.user = JSON.parse(sessionData);
return true;
}
}
¿Por qué esto es poderoso?
- Control Total: ¿Quieres forzar el cierre de sesión de un usuario en todos los dispositivos? Simplemente elimina sus claves en Redis. En el siguiente clic, el Guard rechazará la petición.
- Payload Oculto: Ninguna información del usuario viaja en la cookie. Con JWT, cualquiera que intercepte el token puede decodificar el base64 y leer los datos (aunque no pueda alterarlos sin romper la firma).
El Veredicto Arquitectónico: ¿Cuál elegir?
Como toda respuesta en la ingeniería de software: depende de lo que estés construyendo.
Elige JWT (en Cookies HttpOnly) si:
- Estás construyendo aplicaciones donde el daño de un token robado con una vida útil corta (ej: 15 minutos) es bajo.
- Necesitas escalabilidad extrema y quieres ahorrar llamadas a la base de datos/Redis en cada petición.
- Tu arquitectura involucra docenas de microservicios que necesitan validar la identidad de forma descentralizada.
- Consejo Práctico: Implementa el patrón Refresh Token (también en una cookie HttpOnly) para mantener los JWTs de acceso con una vida muy corta.
Elige Server-Side Sessions (Redis) si:
- Estás construyendo fintechs, sistemas de salud o plataformas con estrictas reglas de compliance.
- Necesitas la capacidad de revocar el acceso de forma instantánea (ej: botón "Cerrar sesión en todos los demás dispositivos").
- Necesitas rastrear activamente cuántas sesiones simultáneas tiene abiertas un usuario.
El Papel Fundamental de Next.js
Independientemente de la elección en tu API (NestJS), la arquitectura de Next.js con el App Router ha simplificado drásticamente la gestión de credenciales en el frontend.
Al usar los React Server Components y las Server Actions, abstraes la complejidad del navegador. El cliente no necesita saber sobre JWTs, librerías de interceptores de Axios o lógicas complejas de Refresh Token. El navegador solo maneja una cookie invisible, mientras que el servidor Node de Next.js hace el trabajo pesado de comunicarse con tu infraestructura backend.
La seguridad de tu aplicación no se trata solo del algoritmo de encriptación que elijas, sino de la superficie de ataque que expones. Y en el frontend moderno, cuanto menos sepa el JavaScript del cliente, más seguro estará tu usuario.
Comentarios
Cargando comentarios...
Únete a la conversación
Inicia sesión con tu cuenta para comentar este artículo.