Comprendiendo los Tokens JWT: Una Guía Completa

· 12 min de lectura

Tabla de Contenidos

¿Qué son los JSON Web Tokens (JWT)?

Los JSON Web Tokens (JWT) se han convertido en el estándar de facto para transmitir información de forma segura entre partes en aplicaciones web modernas. Definidos por RFC 7519, los JWTs proporcionan un método compacto y seguro para URL para representar reclamaciones que se transferirán entre dos partes.

A diferencia de la autenticación tradicional basada en sesiones donde el servidor mantiene el estado, los JWTs son autocontenidos. Esto significa que el token en sí mismo contiene toda la información necesaria para verificar la identidad y los permisos de un usuario. Esta naturaleza sin estado hace que los JWTs sean particularmente valiosos en sistemas distribuidos, arquitecturas de microservicios y aplicaciones móviles.

Los JWTs sirven dos propósitos principales en las aplicaciones modernas:

La belleza de los JWTs radica en su simplicidad y versatilidad. Funcionan sin problemas en diferentes lenguajes de programación y plataformas, haciéndolos ideales para entornos heterogéneos donde tu frontend podría estar en React, tu backend en Node.js, y tu aplicación móvil en Swift o Kotlin.

Consejo profesional: Aunque los JWTs son increíblemente útiles, no son una solución mágica para todos los escenarios de autenticación. Comprender cuándo usar JWTs versus sesiones tradicionales es crucial para construir aplicaciones seguras y escalables.

Estructura y Anatomía Detallada de JWT

Un JWT consiste en tres partes distintas separadas por puntos (.), formando una estructura que se ve así: xxxxx.yyyyy.zzzzz. Cada sección sirve un propósito específico y juntas crean un paquete de información a prueba de manipulaciones.

El Encabezado

El encabezado típicamente consiste en dos partes: el tipo de token (JWT) y el algoritmo de firma que se está utilizando, como HMAC SHA256 o RSA. Esta información le dice a la parte receptora cómo validar la firma del token.

{
  "alg": "HS256",
  "typ": "JWT"
}

El encabezado luego se codifica en Base64Url para formar la primera parte del JWT. Los algoritmos comunes que encontrarás incluyen:

La Carga Útil

La carga útil contiene las reclamaciones, que son declaraciones sobre una entidad (típicamente el usuario) y metadatos adicionales. Las reclamaciones se categorizan en tres tipos:

Reclamaciones registradas: Estas son reclamaciones predefinidas que no son obligatorias pero recomendadas para proporcionar interoperabilidad. Incluyen:

Reclamaciones públicas: Estas son reclamaciones personalizadas que defines para tu aplicación. Para evitar colisiones, deben definirse en el Registro IANA de JSON Web Token o usar nombres resistentes a colisiones (como URIs con espacios de nombres).

Reclamaciones privadas: Reclamaciones personalizadas creadas para compartir información entre partes que acuerdan usarlas. Estas no son reclamaciones registradas ni públicas.

{
  "sub": "1234567890",
  "name": "John Doe",
  "email": "[email protected]",
  "role": "admin",
  "iat": 1516239022,
  "exp": 1516242622
}

La carga útil luego se codifica en Base64Url para formar la segunda parte del JWT.

Consejo rápido: Nunca almacenes información sensible como contraseñas o números de tarjetas de crédito en las cargas útiles de JWT. Aunque los JWTs están firmados, no están encriptados por defecto, lo que significa que cualquiera puede decodificar y leer la carga útil.

La Firma

La firma se crea tomando el encabezado codificado, la carga útil codificada, un secreto (para algoritmos HMAC) o clave privada (para RSA/ECDSA), y firmándolos usando el algoritmo especificado en el encabezado.

Por ejemplo, si se usa HMAC SHA256, la firma se crearía así:

HMACSHA256(
  base64UrlEncode(header) + "." + base64UrlEncode(payload),
  secret
)

La firma asegura que el token no ha sido alterado. Si alguien cambia incluso un solo carácter en el encabezado o la carga útil, la verificación de la firma fallará.

Ejemplo Completo de JWT

Cuando juntas las tres partes, obtienes un JWT completo que se ve así:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

Puedes decodificar e inspeccionar cualquier JWT usando nuestra Herramienta de Decodificación JWT para ver su encabezado, carga útil y verificar su firma.

Creando y Utilizando JWTs en la Práctica

Crear JWTs es sencillo con las bibliotecas adecuadas. Exploremos cómo generar y usar JWTs en diferentes lenguajes de programación y escenarios.

Creando JWTs en Node.js

La biblioteca jsonwebtoken es la opción más popular para aplicaciones Node.js:

const jwt = require('jsonwebtoken');

// Crear un token
const payload = {
  sub: user.id,
  email: user.email,
  role: user.role
};

const secret = process.env.JWT_SECRET;
const options = {
  expiresIn: '1h',
  issuer: 'myapp.com'
};

const token = jwt.sign(payload, secret, options);

// Verificar un token
try {
  const decoded = jwt.verify(token, secret);
  console.log(decoded);
} catch (error) {
  console.error('Token inválido:', error.message);
}

Creando JWTs en Python

Los desarrolladores de Python típicamente usan la biblioteca PyJWT:

import jwt
import datetime

# Crear un token
payload = {
    'sub': user.id,
    'email': user.email,
    'role': user.role,
    'exp': datetime.datetime.utcnow() + datetime.timedelta(hours=1),
    'iat': datetime.datetime.utcnow()
}

secret = os.environ.get('JWT_SECRET')
token = jwt.encode(payload, secret, algorithm='HS256')

# Verificar un token
try:
    decoded = jwt.decode(token, secret, algorithms=['HS256'])
    print(decoded)
except jwt.ExpiredSignatureError:
    print('El token ha expirado')
except jwt.InvalidTokenError:
    print('Token inválido')

Creando JWTs en Java

Para aplicaciones Java, la biblioteca java-jwt de Auth0 es ampliamente utilizada:

import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.DecodedJWT;

// Crear un token
Algorithm algorithm = Algorithm.HMAC256(secret);
String token = JWT.create()
    .withSubject(user.getId())
    .withClaim("email", user.getEmail())
    .withClaim("role", user.getRole())
    .withExpiresAt(new Date(System.currentTimeMillis() + 3600000))
    .withIssuer("myapp.com")
    .sign(algorithm);

// Verificar un token
try {
    DecodedJWT jwt = JWT.require(algorithm)
        .withIssuer("myapp.com")
        .build()
        .verify(token);
} catch (Exception e) {
    System.out.println("Token inválido: " + e.getMessage());
}

Mejores Prácticas para la Generación de Tokens

Al crear JWTs, sigue estas pautas para asegurar seguridad y confiabilidad:

Consejo profesional: Considera implementar un mecanismo de actualización de tokens usando tokens de actualización. Esto te permite mantener los tokens de acceso de corta duración por seguridad mientras mantienes una buena experiencia de usuario sin reautenticación frecuente.

Flujo de Autenticación JWT Explicado

Comprender cómo fluyen los JWTs a través de tu aplicación es crucial para implementarlos correctamente. Recorramos un flujo de autenticación típico paso a paso.

Autenticación Inicial

  1. El usuario envía credenciales: El usuario envía su nombre de usuario y contraseña al punto final de autenticación (típicamente POST /api/auth/login).
  2. El servidor valida las credenciales: El servidor verifica las credenciales contra la base de datos, verificando que el hash de la contraseña coincida.
  3. El servidor genera JWT: Tras una validación exitosa, el servidor crea un JWT que contiene la identidad del usuario y las reclamaciones relevantes.
  4. El servidor devuelve el token: El JWT se envía de vuelta al cliente, típicamente en el cuerpo de la respuesta. Algunas implementaciones también lo establecen como una cookie HTTP-only.

Solicitudes Subsiguientes

  1. El cliente incluye el token: Para cada solicitud subsiguiente, el cliente incluye el JWT en el encabezado de Autorización: Authorization: Bearer <token>
  2. El servidor valida el token: El servidor extrae el token, verifica su firma y comprueba su expiración.
  3. El servidor procesa la solicitud: Si el token es válido, el servidor extrae la información del usuario de la carga útil y procesa la solicitud en consecuencia.
  4. El servidor devuelve la respuesta: Los datos solicitados o el resultado de la operación se devuelven al cliente.

Flujo de Actualización de Token

Cuando el token de acceso expira, el flujo de actualización entra en acción:

  1. El cliente detecta la expiración: El cliente recibe una respuesta 401 No Autorizado o verifica la reclamación exp del token.
  2. El cliente envía el token de actualización: El cliente envía el token de actualización a un punto final de actualización dedicado (POST /api/auth/refresh).
  3. El servidor valida el token de actualización: El servidor verifica el token de actualización y comprueba si ha sido revocado.
  4. El servidor emite un nuevo token de acceso: Se genera un nuevo token de acceso y se devuelve al cliente.
  5. El cliente reintenta la solicitud original: El cliente usa el nuevo token de acceso para reintentar la solicitud fallida.

Flujo de Cierre de Sesión

Cerrar sesión con JWTs requiere consideración especial ya que los tokens no tienen estado:

  1. El cliente inicia el cierre de sesión: El usuario hace clic en cerrar sesión, activando una solicitud a POST /api/auth/logout.
  2. El servidor invalida el token de actualización: El servidor agrega el token de actualización a una lista de revocación o lo elimina de la base de datos.
  3. El cliente descarta los tokens: El cliente elimina tanto los tokens de acceso como de actualización del almacenamiento.
  4. El cliente redirige: El usuario es redirigido a la página de inicio de sesión o área pública.
Tipo de Token Vida Útil Típica Ubicación de Almacenamiento Propósito
Token de Acceso 15 min - 1 hora Memoria o sessionStorage Autorizar solicitudes API
Token de Actualización 7 días - 30 días Cookie HTTP-only o almacenamiento seguro Obtener nuevos tokens de acceso
Token de ID Igual que el token de acceso Memoria Información de identidad del usuario (OpenID Connect)

Consejo rápido: Implementa la actualización automática de tokens en tu aplicación cliente para manejar la expiración de tokens sin problemas. Esto evita que los usuarios sean desconectados inesperadamente

We use cookies for analytics. By continuing, you agree to our Privacy Policy.