Prolongación automática del vencimiento de JWT (JSON Web Token)

Resuelto maryo asked hace 10 años • 17 respuestas

Me gustaría implementar la autenticación basada en JWT en nuestra nueva API REST. Pero dado que el vencimiento está establecido en el token, ¿es posible prolongarlo automáticamente? No quiero que los usuarios tengan que iniciar sesión cada X minutos si estuvieron usando activamente la aplicación durante ese período. Eso sería un gran fracaso de UX.

Pero prolongar el vencimiento crea un nuevo token (y el anterior sigue siendo válido hasta que caduque). Y generar un nuevo token después de cada solicitud me parece una tontería. Suena como un problema de seguridad cuando más de un token es válido al mismo tiempo. Por supuesto, podría invalidar el antiguo usado usando una lista negra, pero necesitaría almacenar los tokens. Y uno de los beneficios de JWT es que no hay almacenamiento.

Descubrí cómo lo resolvió Auth0. Usan no solo el token JWT sino también un token de actualización: https://auth0.com/docs/tokens/refresh-tokens

Pero nuevamente, para implementar esto (sin Auth0), necesitaría almacenar tokens de actualización y mantener su vencimiento. ¿Cuál es entonces el beneficio real? ¿Por qué no tener un solo token (no JWT) y mantener el vencimiento en el servidor?

¿Hay otras opciones? ¿El uso de JWT no es adecuado para este escenario?

maryo avatar Nov 04 '14 22:11 maryo
Aceptado

Trabajo en Auth0 y participé en el diseño de la función del token de actualización.

Todo depende del tipo de aplicación y este es nuestro enfoque recomendado.

aplicaciones web

Un buen patrón es actualizar el token antes de que caduque.

Establezca la caducidad del token en una semana y actualice el token cada vez que el usuario abra la aplicación web y cada hora. Si un usuario no abre la aplicación durante más de una semana, tendrá que iniciar sesión nuevamente y esta es una experiencia de usuario de aplicación web aceptable.

Para actualizar el token, su API necesita un nuevo punto final que reciba un JWT válido y no vencido y devuelva el mismo JWT firmado con el nuevo campo de vencimiento. Luego, la aplicación web almacenará el token en algún lugar.

Aplicaciones móviles/nativas

La mayoría de las aplicaciones nativas inician sesión una vez y sólo una vez.

La idea es que el token de actualización nunca caduque y siempre se pueda canjear por un JWT válido.

El problema con un token que nunca caduca es que nunca significa nunca. ¿Qué haces si pierdes tu teléfono? Por lo tanto, el usuario debe poder identificarlo de alguna manera y la aplicación debe proporcionar una forma de revocar el acceso. Decidimos utilizar el nombre del dispositivo, por ejemplo, "iPad de maryo". Luego el usuario puede ir a la aplicación y revocar el acceso al "iPad de maryo".

Otro enfoque consiste en revocar el token de actualización en eventos específicos. Un evento interesante es el cambio de contraseña.

Creemos que JWT no es útil para estos casos de uso, por lo que usamos una cadena generada aleatoriamente y la almacenamos en nuestro lado.

José F. Romaniello avatar Nov 09 '2014 23:11 José F. Romaniello

En el caso de que usted mismo maneje la autenticación (es decir, no use un proveedor como Auth0), lo siguiente puede funcionar:

  1. Emitir token JWT con vencimiento relativamente corto, digamos 15 minutos.
  2. La aplicación verifica la fecha de vencimiento del token antes de cualquier transacción que requiera un token (el token contiene la fecha de vencimiento). Si el token ha caducado, primero le pide a la API que "actualice" el token (esto se hace de forma transparente para la UX).
  3. La API recibe una solicitud de actualización del token, pero primero verifica la base de datos del usuario para ver si se ha establecido un indicador de 'reauth' en ese perfil de usuario (el token puede contener una identificación de usuario). Si el indicador está presente, se deniega la actualización del token; de lo contrario, se emite un nuevo token.
  4. Repetir.

El indicador 'reauth' en el backend de la base de datos se establecería cuando, por ejemplo, el usuario haya restablecido su contraseña. La bandera se elimina la próxima vez que el usuario inicie sesión.

Además, digamos que tiene una política según la cual un usuario debe iniciar sesión al menos una vez cada 72 horas. En ese caso, la lógica de actualización del token de API también verificaría la fecha del último inicio de sesión del usuario en la base de datos del usuario y denegaría/permitiría la actualización del token sobre esa base.

IanB avatar Feb 20 '2015 02:02 IanB

A continuación se detallan los pasos para revocar su token de acceso JWT:

1) Cuando inicie sesión, envíe 2 tokens (token de acceso, token de actualización) en respuesta al cliente.
2) El token de acceso tendrá menos tiempo de vencimiento y la actualización tendrá un tiempo de vencimiento más largo.
3) El cliente (frontal) almacenará el token de actualización en su almacenamiento local y el token de acceso en las cookies.
4) El cliente utilizará un token de acceso para llamar a las API. Pero cuando caduque, elija el token de actualización del almacenamiento local y llame a la API del servidor de autenticación para obtener el nuevo token.
5) Su servidor de autenticación tendrá una API expuesta que aceptará el token de actualización, verificará su validez y devolverá un nuevo token de acceso.
6) Una vez que el token de actualización caduque, se cerrará la sesión del usuario.

Avíseme si necesita más detalles. También puedo compartir el código (Java + Spring boot).

Bhupinder Singh avatar Jan 26 '2019 12:01 Bhupinder Singh

Estaba trasteando al mover nuestras aplicaciones a HTML5 con API RESTful en el backend. La solución que se me ocurrió fue:

  1. El cliente recibe un token con un tiempo de sesión de 30 minutos (o el tiempo habitual de sesión del lado del servidor) al iniciar sesión correctamente.
  2. Se crea un temporizador del lado del cliente para llamar a un servicio y renovar el token antes de que expire. El nuevo token sustituirá al existente en próximas convocatorias.

Como puede ver, esto reduce las solicitudes frecuentes de tokens de actualización. Si el usuario cierra el navegador o la aplicación antes de que se active la llamada de renovación del token, el token anterior caducará a tiempo y el usuario tendrá que volver a iniciar sesión.

Se puede implementar una estrategia más complicada para atender la inactividad del usuario (por ejemplo, descuidar una pestaña abierta del navegador). En ese caso, la llamada del token de renovación debe incluir el tiempo de vencimiento esperado que no debe exceder el tiempo de sesión definido. En consecuencia, la aplicación deberá realizar un seguimiento de la última interacción del usuario.

No me gusta la idea de establecer una caducidad prolongada, por lo que es posible que este enfoque no funcione bien con aplicaciones nativas que requieren una autenticación menos frecuente.

t7tran avatar May 21 '2015 03:05 t7tran

Una solución alternativa para invalidar los JWT, sin ningún almacenamiento seguro adicional en el backend, es implementar una nueva jwt_versioncolumna de enteros en la tabla de usuarios. Si el usuario desea cerrar sesión o caducar los tokens existentes, simplemente incrementa el jwt_versioncampo.

Al generar un nuevo JWT, codifique jwt_versionen la carga útil del JWT y, opcionalmente, incremente el valor de antemano si el nuevo JWT debe reemplazar a todos los demás.

Al validar el JWT, el jwt_versioncampo se compara junto con el user_idy se otorga autorización solo si coincide.

Ollie Bennett avatar May 31 '2017 09:05 Ollie Bennett

jwt-autorefresh

Si está utilizando nodo (React/Redux/Universal JS), puede instalar npm i -S jwt-autorefresh.

Esta biblioteca programa la actualización de los tokens JWT en una cantidad de segundos calculada por el usuario antes de que caduque el token de acceso (según el reclamo de exp codificado en el token). Tiene un amplio conjunto de pruebas y verifica bastantes condiciones para garantizar que cualquier actividad extraña vaya acompañada de un mensaje descriptivo sobre configuraciones incorrectas de su entorno.

Implementación de ejemplo completo

import autorefresh from 'jwt-autorefresh'

/** Events in your app that are triggered when your user becomes authorized or deauthorized. */
import { onAuthorize, onDeauthorize } from './events'

/** Your refresh token mechanism, returning a promise that resolves to the new access tokenFunction (library does not care about your method of persisting tokens) */
const refresh = () => {
  const init =  { method: 'POST'
                , headers: { 'Content-Type': `application/x-www-form-urlencoded` }
                , body: `refresh_token=${localStorage.refresh_token}&grant_type=refresh_token`
                }
  return fetch('/oauth/token', init)
    .then(res => res.json())
    .then(({ token_type, access_token, expires_in, refresh_token }) => {
      localStorage.access_token = access_token
      localStorage.refresh_token = refresh_token
      return access_token
    })
}

/** You supply a leadSeconds number or function that generates a number of seconds that the refresh should occur prior to the access token expiring */
const leadSeconds = () => {
  /** Generate random additional seconds (up to 30 in this case) to append to the lead time to ensure multiple clients dont schedule simultaneous refresh */
  const jitter = Math.floor(Math.random() * 30)

  /** Schedule autorefresh to occur 60 to 90 seconds prior to token expiration */
  return 60 + jitter
}

let start = autorefresh({ refresh, leadSeconds })
let cancel = () => {}
onAuthorize(access_token => {
  cancel()
  cancel = start(access_token)
})

onDeauthorize(() => cancel())

descargo de responsabilidad: soy el mantenedor

cchamberlain avatar May 27 '2016 07:05 cchamberlain