DEV Community

Cover image for JWT & Refresh Tokens 馃敀
Jean Carlo Vittory Laguna
Jean Carlo Vittory Laguna

Posted on

JWT & Refresh Tokens 馃敀

JWT o Json Web Token es un est谩ndar para la autenticaci贸n y transferencia segura de datos entre dos partes. Usa la encriptaci贸n en base64 y una firma por medio de una clave privada o publico/privada para poder verificar la validez del token.

Estos tokens cuentan con una estructura en particular

Image description

El header o encabezado contiene la informaci贸n del tipo de token utilizado y el tipo de algoritmo usado para la encriptaci贸n. Por otro lado, el payload o carga 煤til hace referencia a los datos suministrados por el desarrollador y cargados dentro del token en formato JSON. Por ultimo tenemos el signature o la firma que consiste en una clave secreta para ser usada como m茅todo de verificaci贸n de la validez del token.

Para mayor informaci贸n referirse a: https://jwt.io/

Ahora bien, en el contexto de una aplicaci贸n que requiera un proceso de autenticaci贸n y autorizaci贸n de usuarios por lo general hacemos uso de JWT para generar tokens los cuales cargamos, dentro del payload del token, con informaci贸n que nos permita verificar en nuestra base de datos que esa persona sea quien dice ser o cuente con los roles y permisos que se requieren para acceder a un recurso en especifico.

Es importante resaltar que el payload utilizado no debe contener informaci贸n susceptible del usuario como contrase帽as, cuentas bancarias entre otras.

Para lograr esto por lo general tenemos un flujo similar a este

Image description

En la anterior imagen observamos como un cliente inicia sesi贸n dentro de nuestra aplicaci贸n, enviando mediante una solicitud HTTP, un email y una contrase帽a la cual verificamos en nuestra base de datos y si dichos datos coinciden devolvemos un ACCESS_TOKEN el cual el cliente almacenara, ya sea en el local storage o en el session storage, y deber谩 enviar en cada solicitud que realice dentro de nuestra aplicaci贸n para autorizar el consumo de los recursos que requiera.

La forma en como enviar el ACCESS_TOKEN desde el servidor depender谩 de tu aplicaci贸n. Este token puede ser enviado mediante cookies o mediante una respuesta HTTP ordinaria. Cada una tiene ventajas y desventajas que deben ser consideradas.

Este ACCESS_TOKEN contara con un tiempo de expiraci贸n el cual configuraremos de acuerdo a las necesidades tanto de seguridad como de experiencia de usuario que nuestra aplicaci贸n requiera.

Debemos considerar que entre mas largo sea el tiempo de expiraci贸n de nuestros tokens mas vulnerable ser谩 nuestra aplicaci贸n en t茅rminos de seguridad. Por otro lado, entre mas corto sea dicho tiempo, nuestros usuarios deber谩n hacer login nuevamente para obtener un ACCESS_TOKEN nuevo lo cual entra en detrimento de la experiencia de usuario.

REFRESH TOKENS

Hasta este punto no se ha dicho nada diferente a lo que es normalmente un sistema de autenticaci贸n y autorizaci贸n com煤n que implemente JWT.

Los refresh tokens nos permiten lograr que nuestro usuario no tenga que realizar un login nuevamente cada que el ACCESS_TOKEN expira y lo logra mediante el env铆o de un token extra o REFRESH_TOKEN cuando el usuario hace login.

La diferencia a este punto es que ambos tokens tienen un tiempo de expiraci贸n diferente. Mientras el ACCESS_TOKEN puede llegar a tener 20s el REFRESH_TOKEN puede llegar a tener 6 meses o 1 a帽o de expiraci贸n y al vencerse el primer token el usuario utilizara el REFRESH_TOKEN como token de acceso para generar un nuevo ACCESS_TOKEN y de esta forma mantener activa la sesi贸n dentro de nuestra aplicaci贸n.

Es posible llegar a pensar que esta soluci贸n no es realmente 煤til ya que simplemente aumentando el tiempo de expiraci贸n del ACCESS_TOKEN nos ahorrar铆a tener que enviar un segundo token o podr铆amos pensar 驴Qu茅 sucede si el REFRESH_TOKEN es hurtado? 驴No pondr铆amos en riesgo la seguridad de nuestro sistema al tener un tiempo de expiraci贸n tan largo?

驴Por qu茅 enviar dos tokens de acceso?

Es importante resaltar que el REFRESH_TOKEN nos permite manejar un concepto llamado SESIONES dentro de nuestra aplicaci贸n la cual por lo general es una tabla en nuestra base de datos.

La idea de generar un segundo token es poder mediante este token generar un ACCESS_TOKEN nuevo para el usuario en caso de que dicho ACCESS_TOKEN haya sido expirado y solo generaremos un nuevo ACCESS_TOKEN si el usuario nos env铆a el ACCESS_TOKEN expirado y el REFRESH_TOKEN existe dentro de nuestro registro de sesiones y es valido como JWT.

Dicho lo anterior, nuestro proceso de autorizaci贸n ahora no solo consultara la validez del REFRESH_TOKEN y del ACCESS_TOKEN o si el usuario que esta accediendo a los recursos de nuestra aplicaci贸n existe, sino que ahora consultara una tabla llamada SESIONES que almacenara los REFRESH_TOKENS activos y, si dicho token existe y esta activo, generara un nuevo ACCESS_TOKEN para acceder a los recursos de nuestra aplicaci贸n.

De esta forma estaremos generando ACCESS_TOKENS continuamente solo y solo si nuestro usuario cuente con un ACCESS_TOKEN valido y un REFRESH_TOKEN valido.

Debemos resaltar que no es lo mismo validez del token que tiempo de expiraci贸n del token. Nuestro token pudo haber expirado pero sigue siendo valido. Los JWT no pueden ser invalidados y solo podr谩n ser manejados mediante un tiempo de expiraci贸n lo cual no necesariamente quiere decir invalidez

驴Qu茅 sucede si el REFRESH_TOKEN es hurtado? 驴No pondr铆amos en riesgo la seguridad de nuestro sistema al tener un tiempo de expiraci贸n tan largo?

Recordemos que la forma de acceder a nuestra aplicaci贸n es mediante el ACCESS_TOKEN y si este ha expirado generaremos otro nuevo mediante la validaci贸n del REFRESH_TOKEN. Por lo tanto solo podemos generar un nuevo ACCESS_TOKEN si contamos tanto con el ACCESS_TOKEN y el REFRESH_TOKEN valido.

Si un atacante logra hurtar nuestro ACCESS_TOKEN solo tardara 5s, o menos si as铆 lo configuramos, para ser expulsado de la aplicaci贸n ya que no podr谩 generar uno nuevo al no tener el REFRESH_TOKEN.

Por otro lado, si un atacante logra hurtar nuestro REFRESH_TOKEN no podr谩 generar un ACCESS_TOKEN ya que, para generar uno, necesita el primer ACCESS_TOKEN generado al momento de hacer login.

Podemos observar aqu铆 la importancia de tener un buen manejo de seguridad en nuestros tokens como por ejemplo:

  • Configurar tiempos cortos en el ACCESS_TOKEN.
  • Transferir nuestros tokens mediante las cabeceras HTTP.
  • Si usamos cookies asegurarnos de usar la bandera HttpOnly y secure para evitar ataques XSS.
  • Limitar el tiempo de expiraci贸n de nuestras cookies.

Llegados a este punto encontramos un flujo similar a este

Image description

Ahora veremos de que forma, desde el c贸digo, podemos actualizar nuestro ACCESS_TOKEN haciendo uso de nuestro REFRESH_TOKEN.

IMPLEMENTACI脫N

En este ejemplo utilizaremos Node y Express para ejemplificar este proceso y haremos toda nuestra l贸gica dentro del middleware de autorizaci贸n. Debemos recordar que nuestro proceso de autenticaci贸n, donde estamos generando nuestros dos tokens, ya esta configurado y nos centraremos en como actualizar nuestro ACCESS_TOKEN utilizando nuestro REFRESH_TOKEN.

Sin embargo, debemos realizar un peque帽o ajuste a nuestro proceso de autenticaci贸n ya que es aqu铆 donde haremos el registro de nuestro REFRESH_TOKEN en la tabla que anteriormente llamamos SESIONES.

El c贸digo luce algo as铆:

Image description

  1. Podemos observar como nuestro servicio de autenticaci贸n, que es llamado desde nuestro controlador de login, primero valida la existencia del usuario a trav茅s del m茅todo getUserByEmail.

  2. Utilizando la librer铆a bcrypt validamos la contrase帽a y, si este proceso arroja un error, devolvemos a trav茅s del manejador de errores el c贸digo correspondiente.

  3. Si la validaci贸n de la contrase帽a fue bien registramos en nuestra tabla SESIONES, usando el m茅todo createSession, la sesi贸n de nuestro usuario.

  4. Como ultimo paso, firmamos ambos tokens y configuramos el tiempo de expiraci贸n para cada uno. Recordemos que el ACCESS_TOKEN por lo general cuenta con un tiempo muy corto de expiraci贸n y el REFRESH_TOKEN, por el contrario, se configura con un tiempo largo de expiraci贸n.

De esta forma tenemos nuestro proceso de autenticaci贸n ahora configurado para que nos devuelva ambos tokens y nos registre una sesi贸n por cada usuario con un login activo.

Proceso de autorizaci贸n

Por lo general tendemos a realizar el proceso de autorizaci贸n, para cada uno de los recursos de nuestra aplicaci贸n, mediante un middleware. Es en este en donde en este ejemplo realizaremos todo el proceso de validaci贸n y generaci贸n ACCESS_TOKEN.

Una vez nuestro usuario haya accedido a nuestra aplicaci贸n y este haya guardado el ACCESS_TOKEN y el REFRESH_TOKEN utilizaremos nuestro middleware de autorizaci贸n para validar si nuestro usuario cuenta con los permisos suficientes para consultar el recurso que esta solicitando.

Image description

En la anterior imagen observamos todo el proceso de autorizaci贸n completo pero vamos a revisarlo linea por linea

  1. En este ejemplo estamos enviando los tokens por medio de el header Authorization y un header personalizado que llamamos x-refresh-token por lo tanto lo primero que realizamos es la validaci贸n acerca de la existencia o no de ambos tokens. Si estos no existen, enviaremos un error de tipo Forbbiden.

  2. En las siguientes lineas tenemos una segunda capa de validaci贸n en donde, usando la librer铆a JOI, validamos si los datos dentro de estas cabeceras son de tipo string.

  3. dentro del try...catch verificamos el ACCESS_TOKEN y accedemos a los valores payload y expired. Si el payload existe, creamos un valor nuevo dentro del objeto req que llamamos user y lo cargamos con lo que viene en el payload. Por 煤ltimo, ejecutamos la funci贸n next la cual nos permite avanzar al controlador.

    Observemos que existe un @ts-ignore ya que para crear valores dentro del objeto Request de Express debemos realizar una configuraci贸n extra la cual no entra en el contexto de este blog. Si deseas saber m谩s al respecto puedes leer como hacerlo aqu铆

  4. Si ocurri贸 alg煤n problema con la verificaci贸n de nuestro ACCESS_TOKEN nuestro m茅todo verifyJWT nos devolver谩 un objeto con el payload null y el expired jwt expired.

    Image description

  5. Luego de esto validamos de que si nuestra propiedad expired tiene un valor truthy y nuestro REFRESH_TOKEN fue validado con 茅xito validaremos nuestro REFRESH_TOKEN usando el m茅todo verifyWT. Caso contrario devolveremos un objeto con una propiedad payload en null.

  6. Si nuestro REFRESH_TOKEN no fue validado con 茅xito devolveremos un error de Unauthorized.

  7. Caso contrario, avanzaremos con nuestro flujo y consultaremos en nuestra base de datos el sessionId que venia en el payload de nuestro REFRESH_TOKEN y si esta consulta no encuentra ning煤n resultado volveremos a arrojar un error de tipo Unauthorized

  8. Por el contrario, si nuestra consulta encuentra un sessionId activo firmaremos un nuevo ACCESS_TOKEN.

  9. El payload de este nuevo token lo ingresaremos en la propiedad user de nuestro objeto Request

  10. Por 煤ltimo, agregaremos este nuevo ACCESS_TOKEN dentro de la cabecera Authorization concatenando la palabra Bearer al inicio. Finalizaremos ejecutando la funci贸n next para avanzar a nuestro controlador.

De esta forma podemos crear un sistema de tokens el cual hace uso de los REFRESH_TOKEN. Si bien este sistema no es infalible podemos mejorar ciertos aspectos de nuestro sistema como lo hemos visto a trav茅s de esta lectura.

Gracias por leer 馃槉

Top comments (0)