JSON Web Token (JWT) es un estándar abierto basado en JSON para crear un token que sirve para enviar datos entre aplicaciones o servicios y garantizar que sean válidos, fiables y seguros. El uso más común de los JWT es el manejo de autenticación en aplicaciones móviles o web.
Para esto cuando el usuario se quiere autenticar manda sus datos de inicio del sesión al servidor, este genera el JWT y se lo manda a la aplicación cliente, luego en cada petición el cliente envía este token que el servidor usa para verificar que el usuario este correctamente autenticado y saber quien es.
Este escenario no es el único caso para JWT, es posible usarlo para transferir cualquier dato entre servicios de nuestra aplicación y asegurarnos de que sean siempre válido. Por ejemplo si tenemos un servicio de envío de notificaciones de corre electrónico, otro servicio podría enviar una petición con un JWT junto al contenido del mail o cualquier otro dato necesario y que estemos seguros que esos datos no fueron alterados de ninguna forma.
Estructura de un JWT
Los JWT tienen una estructura estándar basada en tres partes:
header.payload.signature
Las primeras dos partes, header y payload, son strings en base64 generados a partir dos JSON. La tercera parte, signature, toma las otras dos partes y las encripta usando un algoritmo, generalmente SHA-256. Ejemplo:
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE1MDE3ODE4MDYsImV4cCI6MTUwMTc4MTg2NiwiZGF0YSI6eyJpZCI6MSwibmFtZSI6IkpvcmdlIn19.y8NLPtvhyOUmU1KDityo5vhjq9xr6eTO2QbPGXlX12E
Header
El header de un JWT tiene la siguiente forma:
{ "alg": "HS256", "typ": "JWT" }
La propiedad alg indica el algoritmo usado para la firma y la propiedad typ define el tipo de token.
Propiedades posibles:
- Tipo de token (typ) – Identifica el tipo de token.
- Tipo de contenido (cty) – Identifica el tipo de contenido (siempre debe ser JWT)
- Algoritmo de firmado (alg) – Indica que tipo de algoritmo fue usado para firmar el token.
Payload
El payload de un JWT es un JSON que puede tener cualquier propiedad, aunque hay una serie de nombres de propiedades definidos en el estándar.
{ "id": "1", "username": "jorge", "iat": 1356999524 }
Propiedades estándar:
- Creador (iss) – Identifica a quien creo el JWT
- Razón (sub) – Identifica la razón del JWT, se puede usar para limitar su uso a ciertos casos.
- Audiencia (aud) – Identifica quien se supone que va a recibir el JWT. Un ejemplo puede ser web, android o ios.
- Quien use un JWT con este campo debe además de usar el JWT enviar el valor definido en esta propiedad de alguna otra forma.
- Tiempo de expiración (exp) – Una fecha que sirva para verificar si el JWT esta vencido y obligar al usuario a volver a autenticarse.
- No antes (nbf) – Indica desde que momento se va a empezar a aceptar un JWT.
- Creado (iat) – Indica cuando fue creado el JWT.
- ID (jti) – Un identifador único para cada JWT.
Signature
La firma del JWT se genera usando los anteriores dos valores en base64 y una key secreta (solo la conocerá los servidores que creen o usen el JWT) para usar un algoritmo de encriptación.
key = 'secret' unsignedToken = base64Encode(header) + '.' + base64Encode(payload) signature = SHA256(key, unsignedToken) token = unsignedToken + '.' + signature
Implementación en PHP
En este ejemplo vamos a autenticar a un usuario usando JWT, para esto debemos agregar la dependencia mediante Composer.
composer require firebase/php-jwt
Si no tiene instalado composer en su servidor ver el siguiente enlace.
Generando token
Crearemos el siguiente script
require_once 'vendor/autoload.php'; use Firebase\JWT\JWT; $time = time(); $key = 'secrete_key'; $token = array( 'iat' => $time, // Tiempo que inició el token 'exp' => $time + (60*60), // Tiempo que expirará el token (+1 hora) 'data' => [ // información del usuario 'id' => 1, 'name' => 'Jorge' ] ); $jwt = JWT::encode($token, $key); $data = JWT::decode($jwt, $key, array('HS256')); var_dump($data);
- JWT::encode recibe 2 parámetros, el primero es la información que queremos que contenga nuestro token y el otro es una cadena que usaremos como key para evitar que otro pueda decodificar nuestro token.
- La información que contendrá nuestro token esta compuesto por los siguientes parámetros.
- – iat: es el tiempo que el token inició.
- – exp: es el tiempo que el token expirará.
- – data: es información que queremos que guarde el token, en nuestro caso la información del usuario.
Ejecutamos el script
object(stdClass)#2 (3) { ["iat"]=> int(1501793288) ["exp"]=> int(1501793348) ["data"]=> object(stdClass)#4 (2) { ["id"]=> int(1) ["name"]=> string(5) "Jorge" } }
Validando token
En esta validación, el key ingresado para decodificar el token no es el mismo que se usó para codificarlo
$jwt = JWT::encode($token, $key); $data = JWT::decode($jwt, 'xxxx', array('HS256'));
Nos mostraría el siguiente mensaje: ‘Signature verification failed’
En la siguiente validacion restaremos un segundo a la fecha de expiracion
'exp' => $time--,
Nos mostraría el siguiente mensaje: ‘Expired token’
Mediante el uso de JWT se hace práctico implementar la autenticación de nuestras aplicaciones, servicios, apis, etc. Como principal desventaja, es que no existe manera de cancelar un JWT, con lo cual se deben asignar tiempos de expiración bastante ajustados para evitar que en caso de que un token haya sido comprometido pueda ser utilizado de forma indefinida.
Enlace de referencia:
https://jwt.io/