Cómo usar JWT en Javascript
La autenticación y la autorización son elementos indispensables que tenemos que solventar siempre que estamos desarrollando una aplicación web. Una de las tecnologías más usadas para este propósito es JSON web Tokens (JWT).
¿Qué es JWT?
JSON web token es un estándar abierto que define una forma de transmitir información segura entre dos partes usando un objeto JSON.
Una de las características de JWT es que la información que mandamos puede ser verificada ya que está firmada digitalmente, por lo que si la firma es correcta podremos confiar en la información recibida.
Un JWT está compuesto por tres partes: encabezado (x), carga útil (y) y la firma (z). Cada una de estas partes está separada por un punto.
xxxxxxxxxx.yyyyyyyyyyy.zzzzzzzzz
Encabezado
Esta parte del token suele contener información sobre el token en sí, describiendo por ejemplo el algoritmo o el tipo de token.
{
"alg": "HS256",
"typ": "JWT"
}
Carga útil
Aquí es donde añadimos la información que queremos transmitir. Algo habitual es indicar el ID del usuario, rol o cualquier otra información relevante.
Es importante saber en este punto que debemos tener cuidado con la información que transmitimos en un JWT ya que esta no está encriptada, solo codificada y firmada (profundizaremos en esto un poco más adelante)
{
"id": "1234567890",
"username": "ejemploUsuario",
"rol": "admin"
}
Firma
La firma la usaremos para verificar que el remitente del token es de quien dice ser y para garantizar que la carga útil no ha sido manipulada.
Esta firma se genera combinando el encabezado y la carga útil codificados en base64, junto con una clave secreta y el algoritmo de codificación que definimos en el encabezado.
Codificado y firmado
Como adelantábamos unas líneas arriba, los JWT no están encriptados sino codificados y firmados. Esto significa que cualquiera puede leer el contenido del JWT, pero no puede alterarlo sin que se note.
La codificación que usan los JWT es base64url, lo que hace que el no sea fácilmente comprensible por humanos, aunque es fácilmente descodificarlo.
Dicho esto, es importante que tengas en cuentas estas normas básica de seguridad a la hora de emplear JWT:
- No almacenar información sensible en la carga útil.
- Usa siempre HTTPS para proteger la transmisión entre cliente y servidor
- Usa claves y algoritmos de firma fuertes para firmar tus JWT
- Añade tiempo de expiración cortos a tus tokens y/o mecanismos de revocación.
Usando JWT en javascript (node)
A continuación veremos como podemos crear un token y posteriormente verificar la firma. Para ello usaremos la librería jsonwebtoken
Para instalar la librería usaremos un gestor de paquetes como npm:
npm install jsonwebtoken
Generación de claves RSA
Para usar el algoritmo RS256 necesitamos un par de claves RSA, una clave privada y otra pública.
Usaremos el comando openssl en nuestra consola para generarlas.
Primero crearemos la clave privada:
openssl genpkey -algorithm RSA -out private.key -pkeyopt rsa_keygen_bits:2048
A continuación generaremos la clave pública basada en la clave privada creada previamente:
openssl rsa -pubout -in private.key -out public.key
Con esto generaremos los dos ficheros necesarios que usaremos para firmar (privada) y para verificar (pública)
.
├── private.key
└── public.key
Creación de token
Como vamos a usar node y ECMAScript crearemos un fichero index.mjs con el siguiente código donde usaremos la clave privada para firmar el payload (carga útil) contenida en el objeto usuario mediante el método sign de la libería jsonwebtoken.
Al firmar el token le establecemos el algoritmo RS256 y una expiración de 1 hora
import jwt from 'jsonwebtoken';
import fs from 'node:fs';
const usuario = {
id: 1,
nombre: 'Javascript.com.es',
rol: 'admin'
};
const clavePrivada = fs.readFileSync('private.key', 'utf8');
const token = jwt.sign(usuario, clavePrivada, { algorithm: 'RS256', expiresIn: '1h' });
console.log(token);
Si todo ha ido bien tendremos un token como el siguiente:
eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwibm9tYnJlIjoiSmF2YXNjcmlwdC5jb20uZXMiLCJyb2wiOiJhZG1pbiIsImlhdCI6MTcxMzcyNDE5MywiZXhwIjoxNzEzNzI3NzkzfQ.GzxmZzO28lBTyrec-Sw-vyRZzJYQYfgp_5SESbSQyH6-Oq6rz8_o3P7kvftX2SnvSlD6QvDIfUD9Pzbluxz728U9V5usjYVF14ji1ssBXP4QfFfxxAt6bIpOqGroeJJ-t3xdcwwx4lKp1KDJHijk9mXekeKqJt2ySuYFrgNlLNRf9tBCxwE21fbgLt9iEcTbzswjIv5eOrui4WGir9LPdFQuFZCaRzNwokVRsd6tkWjlGcRLka7MXKCRv38vv_XBs7CY696X1WMiNMjZq5QKSPr9-wh4ueE8QM0cpxMC-DDqDGvQ7f3WUaWqwLElTKvnPV2ACSj5EUc2h-zKsr0pTw
Puedes usar aplicaciones como jwt.io para comprobar la información del token.
Verificación del token
Para verificar el token necesitaremos usar de nuevo la librería jsonwebtoken así como la clave pública. Usamos el método verify para comprobar si la firma concuerda con el contenido del token, o dicho de otra forma, nos asegura que el token no se ha modificado.
import jwt from 'jsonwebtoken';
import fs from 'node:fs';
const token = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwibm9tYnJlIjoiSmF2YXNjcmlwdC5lcyIsInJvbCI6ImFkbWluIiwiaWF0IjoxNzEzNzI0MTkzLCJleHAiOjE3MTM3Mjc3OTN9.GzxmZzO28lBTyrec-Sw-vyRZzJYQYfgp_5SESbSQyH6-Oq6rz8_o3P7kvftX2SnvSlD6QvDIfUD9Pzbluxz728U9V5usjYVF14ji1ssBXP4QfFfxxAt6bIpOqGroeJJ-t3xdcwwx4lKp1KDJHijk9mXekeKqJt2ySuYFrgNlLNRf9tBCxwE21fbgLt9iEcTbzswjIv5eOrui4WGir9LPdFQuFZCaRzNwokVRsd6tkWjlGcRLka7MXKCRv38vv_XBs7CY696X1WMiNMjZq5QKSPr9-wh4ueE8QM0cpxMC-DDqDGvQ7f3WUaWqwLElTKvnPV2ACSj5EUc2h-zKsr0pTw"
const clavePublica = fs.readFileSync('public.key', 'utf8');
jwt.verify(token, clavePublica, { algorithm: 'RS256' }, (error, decoded) => {
if (error) {
console.log('Error:', error.message);
} else {
console.log('Token decodificado:', decoded);
}
});
De este modo, si la firma es correcta obtendremos el token decodificado o de lo contrario un error.
Token decodificado: {
id: 1,
nombre: 'Javascript.com.es',
rol: 'admin',
iat: 1713724193,
exp: 1713727793
}
Como puedes observar, utilizar JWT es una tarea bastante simple que nos garantiza que la información transmitida entre el cliente y el servidor no ha sido alterada, permitiéndonos confiar en que realmente corresponde al usuario que el token indica.