Autenticación con JSON Web Tokens

JSON Web TokensLos JSON Web Tokens (JWT) se ha convertido rápidamente en un estándar en la autenticación de aplicaciones, pues permite de una forma simple y elegante identificarte con el servidor mediante un Token. Dicho token es generado por el servidor y es transmitido hacia el cliente, el cual deberá presentar en cada invocación para poder ser autenticado.

Que es un Token

Lo primero que debemos de entender, es que es un token, pues será un concepto fundamental para entender el resto del artículo.

Un token es una cadena alfanumérica con caracteres aparentemente aleatorios, como el siguiente:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ

o el siguiente:

24353689

Estas cadenas de texto, pueden no aparentar un significado, sin embargo, tiene un significado real para el servidor o institución que lo emitió, el cual puede entender y así, validar al usuario que intenta acceder a la información, e incluso, puede tener datos adicionales.

Te dejo un video donde explico que es un Token:

Un caso simple de tokens, es el dispositivo que dan los bancos para realizar transacciones desde internet, este token te genera un valor numérico que luego tenemos que ingresar al sistema, para que de esta forma, el portal pueda asegurarse de que efectivamente somos nosotros y no un impostor.

En el caso de los tokens bancarios, no se almacena una información real dentro del Token, sino que simplemente es un valor generado que luego puede ser validado por el banco como un valor real generado por nuestro token. Sin embargo, con JWT podemos enviar cualquier dato del cliente dentro del token para que el servidor pueda obtener mucha más información de nosotros.

Que son los JSON Web Tokens

Bien, una vez que comprendemos que son los Tokens podemos decir que los JWT son un tipo de token el cual engloba una estructura, la cual puede ser desencriptada por el servidor y de esta forma, autenticarnos como usuario en la aplicación.

Veamos la estructura de un JWT:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ

Observemos que la cadena está dividida en 3 secciones separadas por un punto. Estas tres secciones tienen un significado:

  1. HEADER: la primera parte, corresponde a los Header, y se almacena por lo general el tipo de token y el algoritmo de encriptamiento.
  2. PAYLOAD: La segunda parte, contiene los datos que identifican al usuario, como puede ser su ID, nombre de usuario, etc.
  3. FIRMA: La tercera parte es la firma digital, la cual se genera con las secciones anteriores y sirve para validar que el contenido no haya sido alterado.

Ejemplo del Header

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

Ejemplo del Payload

{
  "id": "1234567890",
  "name": "Oscar Blancarte",
  "rol": “admin”
}

La firma por otra parte es el header y el payload en base64 y después encriptado.

Puedes leer más de JWT en su página oficial

Como se utiliza los JWT

Como ya hablamos, los JWT se utilizan para autenticar a los usuarios, para ello, el usuario requiere de un login tradicional como es el usuario y password. Una vez, que el sistema de Backend valida que el usuario y contraseña son correctos, este retorna un token al usuario. Este token lo deberá guardar el cliente, pues de aquí en adelante, todas las peticiones que realice al servidor, deberá llevar el token.

JWT JSON Web Token

El token es por lo general almacenado en Cookies o en el LocalStorage del navegador, y cuando es requerido enviar un request al servidor, se recupere y se envía como header.

Un dato interesante del token es que no requiere que el servidor lo almacene para compararlos cuando lo envíe el cliente, pues el token por si solo puede ser auto validado, y como tiene un payload, es posible determinar de quien es el token. Veamos el siguiente payload:

{
  “userID”: 12345,
  “username”: “oblancarte”,
  “rol”: “admin”
}

Cuando el servidor desencripte el token, podrá recuperar este payload y con ello, podrá saber que usuario es e incluso, es posible guardar datos adicionales como el rol o cualquier dato en formato JSON.

Otro dato interesante del token es que expira, es decir, el token solo es válido por un tiempo determinado. Por lo que el usuario deberá volverse a logear una vez que el token expire.

Veamos cómo funcionaría todo el ciclo de vida de un JWT

Ya hemos hablado de como JWT funciona, pero ahora entraremos a ver como es la interacción que tiene un usuario al autenticarse por medio de JWT, para lo cual veamos la siguiente imagen:

JWT JSON Web Token-lifecycle

Los pasos son los siguientes:

  1. El usuario requiere de una autenticación tradicional con el servidor, es decir usuario y password (o cualquier otro tipo de autenticación).
  2. El servidor validará que los datos introducidos sean correctos y generará un Token.
  3. El servidor enviará el token al usuario y este lo tendrá que almacenar de cualquier forma.
  4. Una vez con el token, el usuario realiza una petición al servidor, enviando en el header el token previamente generado.
  5. El servidor validará que el token sea correcto, desencriptandolo mediante la misma llave que utilizo para encriptarlo.
  6. Si el token es correcto, entonces el servidor retornará los datos solicitados.

Cabe mencionar que los puntos 4,5 y 6 se pueden repetir indeterminado número de veces hasta que el token caduque, cuando esto pase, entonces será necesario reiniciar desde el paso 1.

Como implementar un JWT

Existe una serie de librerías que nos permite crear un JWT, y todo dependerá del lenguaje que utilicemos. Sin embargo, quiero mostrarte como crear un JWT con NodeJS.

Lo primero, es que será necesario importar el módulo jsonwebtoken:

npm install –save jsonwebtoken

 

Generación del Token

ya con la dependencia instalada podemos crear el token de la siguiente manera:

var jwt = require('jsonwebtoken')

function generateToken(user) {
  var u = {
   username: user.username,
   id: user.id
  }
  return token = jwt.sign(u, ‘password’, {
     expiresIn: 60 * 60 * 24 // expires in 24 hours
  })
}

Lo primero que haremos será crear una función que reciba el objeto usuario o cualquier otro objeto que contenga la información requerida para la autenticación. Luego mediante el objeto usuario, creamos el objeto u, el cual solo contiene el username y el ID del usuario. Este objeto se convertirá en el payload del JWT, es decir la segunda parte del token.

Seguido, procedemos a crear el token mediante el módulo jwt, el cual nos proporciona el método sign que recibirá el payload y un password, este password deberá ser secreto, pues con él, podremos crear y desencriptar los tokens. Finalmente, el tercer parámetro son las opciones, aquí podremos seleccionar el algoritmo de encriptación, la fecha de expiración, etc.  En este ejemplo dejamos la configuración por default, pero agregamos que el token sea válidos solo por 24 horas.

Finalmente, el token es retornado al cliente.

Validación del token

Hasta este punto solo hemos visto la parte de la generación del Token, pero falta la segunda parte, en donde tenemos que validar que el Token que el cliente nos manda, es válido.

Para validar el Token podemos utilizar un Middleware de Express que valide de forma automática todas las URL que inicien con /secure. El middleware se ve de la siguiente manera:

router.use('/secure',function(req, res, next) {
  var token = req.headers['authorization']
  if (!token) {
    res.status(401).send({
      ok: false,
      message: 'Toket inválido'
    })
  }

  token = token.replace('Bearer ', '')

  jwt.verify(token, ‘password’, function(err, token) {
    if (err) {
      return res.status(401).send({
        ok: false,
        message: 'Toket inválido'
      });
    } else {
      req.token = token
      next()
    }
  });
});

Podemos ver que en la primera llínea inicializa el middleware, luego en la segunda línea obtenemos el token del header authorization”. Si el token es Null regresamos un 401 indicando que no el cliente no tiene privilegios.

Los token generados por JWT se crean con la palabra “Bearer” al inicio del token, por lo que en la línea 10 quitamos esa parte del token, seguido, en la línea 12 realizamos la validación del token mediante el método verify.

Este método recibe el token como primer parámetro, como segundo parámetro, mandamos el password para desencriptar el token. Finalmente se envía como tercer parámetro una función Callback una vez que el token sea validado.

Si todo salió bien, el token estará en la variable token, en caso contrario, tendremos el error en la variable err.

Conclusiones

Como pudimos ver, los tokens son una magnifica forma de administrar la autenticación de los usuarios, pues permite crear de forma fácil, un mecanismo para comunicarse de forma segura entre el cliente y el servidor, además, de que permite mantener autenticado a un usuario por un tiempo determinado, evitando tener que iniciar sesión cada vez que entre a nuestra aplicación.

70 thoughts to “Autenticación con JSON Web Tokens”

      1. Hola Miguel,

        Esa es una muy buena pregunta. Primero que nada, si es posible utilizar JWT entre servicios. Claramente, el servicio al que te conectas debe de emitir el token, y en cada invocación de los clientes, estos, deben de enviar el token.

        Lo único a considerar, es tener cuidado con la expiración de los tokens, pues entre servicios no existe el login, por lo que tienes dos opciones, y le pones que nunca caduca, o debes detener cuidad de actualizar el token antes que caduque, pues si no, van a fallar todas las ejecuciones.

        Espero que esto resuelva tu duda.

        1. Muy buen post…excelente ejemplo para explicar el uso de jwt. Yo lo he usado en algunas apis pequeñas que he desarrollado. Saludos.

          1. Gracias por el comentario Marcos, lo puede usar en API grandes o pequeñas, en realidad es lo mismo, pero siempre es bueno comenzar con una pequeña

    1. Hola Eider, esa es una buena pregunta.
      Primero que nada, la única forma que un token se puede ver comprometido, es si descubren el password con el que fue cifrado, en tal caso, TODOS los tokens estuvieran comprometidos. En este caso no queda más que cambiar el password de cifrado, invalidando automáticamente todos los tokens que ya generaste.

      Ahora bien, si tu quieres tener una doble validación, puede guardar del lado del backend el token que le emitiste a cada usuario, de esta forma, cada vez que el usuario se autentifique, puedes primero que nada, validar que el token es valido y luego puedes ir a la Base de datos para validar si efectivamente ese token es válido. Desde la base de datos tu podrías agregar un campo que indique si el token sigue siendo valido o no. Así, si tu detectas alguna irregularidad, simplemente marcas ese token como invalido, forzando al usuario a volverse autenticar.

      Espero que esto resuelva tu duda.

  1. Saludos.

    He revisado diversos articulos en la red, me llama mucho la atención JWT por la seguridad, aparte de este, estoy leyendo otros, pero voy a ser sincero, he buscado de implementar los ejercicios y ninguno camina, realmente no entiendo, o es mucha información que no proceso, o falta información, por casualidad tendrá en su catalogo de publicaciones un tutorial con java para manejar jwt. Para estudiar y realizar ejercicios a ver que tal.

    Mil Gracias.

    1. Hola Dustin, desde luego que implementar una tecnología nueva es complicado, sobre todo, si no tiene conocimientos previos.
      De momento no tengo ningún articulo sobre como implementar JWT en Java, te puedo recomendar mi libro https://reactiveprogramming.io/ donde hablo de como crear un API REST utilizando NodeJS + JWT, además que enseño a React y MongoDB, quizás te pueda ser de utilidad.
      saludos.

  2. Buenas, he leído varios artículos sobre el tema y me genera una duda, si alguien da con mi token (ya que se dice que se puede guardar en el localStorage del dispositivo para hacer futuras peticiones al servidor) al hacer una petición desde otro dispositivo pero pasando por cabecera mi token (que es por donde se suele pasar) no estaría teniendo acceso a mis privilegios? Se puede hacer? Muchas gracias.

    1. Hola Miguel, esa es una EXCELENTE pregunta que nadie me la había echo y la respuesta es muy simple, SI, si alguien tiene tu token, automáticamente tiene acceso a tus privilegios, sin embargo…, siempre ha existido ese riesgo de seguridad, solo para poner un ejemplo, cuando utilizas Java o Net o cualquier otro lenguaje que crea sesiones del lado del servidor, este te asigna una Cokie, en el caso de Java es la famosa JSESSION, ahora bien, si alguien copiara esa cockie, tendría igualmente todos tus privilegios.

      Ahora bien, podrías argumentar diciendo que las sesiones del servidor caducan, y es verdad, pero igualmente un token también caduca, en tal caso, podrías crear tokens con una duración de 30 min, 1 hora, etc.
      Como vez, este hueco en la seguridad siempre ha estado presente, solo que no es tan evidente debido a que los servidores tradicionales crean las cokies de forma automática y no somos concientes de ello, en cambio, en JWT, el programador es el encargado de guardar manualmente en el Local Storage y es donde ahora si, somos cocientes del problema.

      De igual forma, te recomiendo que veas mi libro https://reactiveprogramming.io/ donde explico como implementar correctamente JWT en un API REST 🙂

      Espero que esta respuesta resuelva tu duda.

  3. Buenas amigo, disculpa ando haciendo un cms con php y angularjs 1.6 pero tengo una duda como le hago para cerrar session luego de haber creado el token? con un boton

    1. Hola Jesús, recuerda que al crear un token, no estas creando una sesión, simplemente estas emitiendo un Token, con el cual se te puedes autenticar en el futuro. Una vez que es emitido un token, no podrás quitarlo al usuario, podrás intentarlo, pero el siempre podrá guardar un respaldo de el. Lo que tiene que hacer en tal caso, es guardar el token emitido en la base de datos y desde allí marcarlo como anulado. así, la próxima vez que quiera autenticarse, podrás validar el token contra el registro en la base de datos, si esta anulado, simplemente le restringes el acceso.

  4. Hola, me llamo Miguel, y navegando buscando respuesta , descubrí tu blog.
    Tras leer tu excelente articulo, me queda una duda, ¿ en que header debo enviar el Token para la autenticacion ?
    Yo utilizo : Authentication : , pero esto no funciona.
    ¿ Me das alguna idea o donde estudiarlo?
    Gracias por mostrar tu trabajo.

  5. Estupendo, muy didáctico gracias!
    Saludos!

    ————————————————————–
    Un pequeño comentario, que espero no te moleste.

    En:

    3. FIRMA: La tercera parte es la firma digital, la cual se genera con las secciones anteriores y sirve para validar que el contenido no allá sido alterado.

    sería “haya sido alterado”

  6. Hola Oscar, ya ha pasado tiempo, espero te llegue mi pregunta,

    Puedo manejar JWT a nivel de @Controllers ? utilizando la anotación @PreAuthorize(“hasRole(‘AlgunRole’)”) en los @RestController funcional muy bien, pero con los @Controllers es posible ?

    Gracias.

    1. Hola Gerardo, JWT es una librería independiente del framework, podrías utilizar JWT en cualquier parte, incluso clases normales sin anotar, quizás lo interesante no es si lo puedo utilizar allí, si no si es la mejor parte para recuperar los headers, validar el token, etc, pero eso solo tu podrás saber dependiente el tipo de requerimiento que tengas.

  7. Una duda, hay alguna propiedad para poder generar el mismo token varias veces?
    es decir que no cambie cada vez que lo genero con los mismos claims y la llave

    1. Hola Genaro, si ningún dato cambia durante la generación, si que podrás generar un igual, pero por lo general, todos los tokens cambios por la vigencia, lo que hace que el token cambie, sin embargo, si no cambias ningún valor, te debería de generar uno igual. aun que no comprendo para que querrías hacer tal cosa

  8. hola oscar, saludos, excelente post.

    mi pregunta es la siguiente: con php y sesiones podemos controlar bloques de codigo, ejemplo,
    <?php
    if(isset($_SESSION['rol']=='admin' )
    echo — MOSTRAR CIERTO BLOQUE HTML, LOGICA ADICIONAL, incluir o no otros archivos PHP U OTROS ASPECTOS
    ?>

    podemos hacer lo mismo con JWT en caso de que el token sea VALIDO?, independientemente si es con php o nodejs.

    podemos controlar estructuras HTML con JWT como lo hacemos con sesiones ?

    1. Hola Miguel, el token tiene información para determinar los privilegios concedido al momento de crear el token, por lo tanto, puedes obtener esa información y basado e ello, determinar que mostrar en pantalla.

      saludos.

      1. gracias por la respuesta oscar, de verdad excelente POST.

        y esta validación que te comento tendria que ser logicamente con JS.

        tienes algunos otros link de JWT ?

        1. Depende de la tecnología que estás utilizando, si son tecnologías que construyen la vista en el backend, como PHP, tendrás que hacer la valdiación desde el backend, pero si son tecnologías como React que construyen la vista en el mismo navegador, entonces tendrás que hacerla con JS.
          tengo otro artículo donde explico como implementar JWT en NodeJS https://www.oscarblancarteblog.com/2018/01/16/implementar-json-web-tokens-nodejs/ y mi libro, donde lo explico aun mejor https://reactiveprogramming.io/books/applicaciones-reactivas-con-react-nodejs-mongodb

          saludos.

  9. Hola Oscar,

    En mi empresa he diseñado el hardware y el software de microcontroladores de nuestros equipos, pero un modelo de equipo lo tengo conectados por puerto serie a un pc miniitx donde corro linux (en c y python) para presentar mas visualmente los resultados.

    El tema es que ahora tengo un posible cliente que quiere que interactue desde ese pc con su api en json y jwt, y estoy perdidisimo.

    He leido unas cuantas paginas pero no acabo de ver ninguna explicacion, ejemplo, o tutorial donde se explique como hacer la parte de cliente en python, c o algo que no me complique mucho en linux.

    Todo lo que veo incide mas en el servidor, y pienso que la parte cliente no ha de ser muy complicada pero no veo por donde empezar.

    Tienes algun ejemplo de esa parte, o links a tutoriales o lo que sea.

    Gracias por anticipado.

  10. Hola en las librerias para generar JSON Web Tokens existe alguna forma de especificarle el tamaño del token que se desea que esta genere en la API Rest, el problema es que para interactuar con micro controladores un token de gran tamaño se vuelve un problema en ocasiones… Gracias por el POST

    1. Fijate que nunca he tenido ese problema, pero supongo que JWT no se presta para eso, pues el token se serialza basado en los datos que contiene, por lo tanto a mayor sea la cantidad de datos, mas grande es la cadena, quizás deberías de buscar otra tecnología para generar tokens que los cree con una longitud fija

  11. buenas tardes Oscar
    Ante todo felicitarte por tu blog, es sorprendentemente muy bueno.
    Quisiera hacerte una pregunta por si me puedes ayudar.
    Estamos usando la nueva funcionalidad de llamadas API rest desde host y nos estamos encontrando con algún problema con los tokenes jwt generados desde host. Hay veces, no todos, que se generan en host pero a la hora de validarlos desde Darwin o desde la pagina jwt.io nos da problemas en la validación de la firma, cuando la validación desde el servicio de host da ok.

    Le hemos dado unas vueltas y no vemos el problema, si está en la generación de esa firma desde host o donde puede estar??? , pero no podemos ver mucho mas.

    Agradezco de todo corazón si nos puedes echar una mano
    gracias !!!

    1. Lo único que puedo detectar con lo que me mencionas, es que estes generando una llave secreta para el firmado de forma aleatoria, o que se genera de forma automática al momento de inciar la aplicación, esto lo puede comprobar si al reiniciar el servidor, los token generados anteriormente dejan de funcionar. fuera de eso, la verdad no tengo idea de lo que esté pasando, pueden ser tantas cosas.
      Si te sirve de algo, te invito a que veas mi curso de desarrollo de API REST con Spring Boot donde explico como crear un API desde cero y utilizando la autenticación mediante Tokens

  12. la clave de firmado en origen siempre es la misma. Lo extraño es que de 20 veces nos falla 1.
    Era por si os había pasado algo de este tipo de problemas.

  13. Hola, excelente la información. Es posible utilizar JWT entre aplicaciones, me explico en una mi web genero su token y quiero validar ese mismo token en otra aplicación interna para evitar el segundo logueo.

  14. Hola a todos, primero de todo muchas gracias por compartir tanta información, no es la primera vez que acabo en una de tus páginas y terminan resolviéndome el problema.

    Te explico, tengo un servidor que ofrece interfície gráfica a través de la web y una api para servicios. La empresa me ha pedido que usara identificación basada en JWT pero creo que para la conexión con navegador esto no es posible puesto que debería usar sesiones. He pensado en implementar dos tipos de identificaciones, una basada en sesiones y la otra en JWT pero no estoy seguro si es la mejor opción. Que opinas?

    1. El token y la sesión no están peleados, el token se puede utilizar únicamente para autenticar al usaurio, pero una vez autenticado, puedes seguir utilizando la sesión del lado del servidor.

  15. const url= ‘http://1…………….’;
    console.log(‘dataURL: ‘+ url);
    const request= new XMLHttpRequest();
    request.open(‘GET’, url);
    request.onload= function (){
    const dato = JSON.parse(this.responseText);

    const gifso = dato.map(img => {
    return{
    userName: img.userName,
    passport: img.passport,
    likes: img.likes,
    }
    })
    console.log(gifso);

    }
    request.send();

  16. Oscar, te agradezco el tiempo que te llevó hacer este documento y además que hayas compartido tus conocimientos con la comunidad. Acabo de recibir un cliente que me envía un Token y que debo procesar en PHP. Antes de tu documento estaba en medio del mar sin saber hacia donde ir.
    En mi caso tengo que validar el Token que recibo un Token en $body[‘Token’] y tengo el Key.
    Dentro de la documentación que tengo aparece la siguiente función:

    function validate_jwt_token($token, $key) {
    // Validate token with JWT (see: https://jwt.io/ )
    }

    Ya me aseguré de que a la función le llegue el Token y Key correspondientes.
    Veo que existe una función de JWT que sería: JWT::decode($jwt,$key,array(‘HS256’));
    Pero hasta ahí he llegado. Los datos los pongo en $decoded = JWT::decode($jwt,$key,array(‘HS256’));
    No se como informarle al Servidor que si está correcto el Token.
    ¿Tendrás algún video de ayuda para PHP o si sabes de algún lugar donde pueda buscar?

    Pongo mi código por si alguien más está tratando de resolver por PHP.

    Gracias de antemano por leer este mensaje.
    Saludos.

      1. Cómo definimos.el.token en la blockchain ? Si el token representa un bien físico, esa info del token como es almacenada ? El token sería una transacción del bloque ?

        1. Hol Emi, creo que estas bastante perdida, olvida el concepto de la blockchain, aquí el token es solo una cadena que texto que sirve para validar al usuario. Ese toquen lo podrías guardar en la base de datos, así de simple

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *