Me gustaría implementar la autenticación basada en JWT en nuestra nueva API REST. Pero dado que la caducidad se establece en el token, ¿es posible prolongarlo automáticamente? No quiero que los usuarios necesiten iniciar sesión después de cada X minutos si estaban usando activamente la aplicación en ese período. Eso sería un gran error de UX.
Pero prolongar la caducidad crea un nuevo token (y el antiguo aún es válido hasta que caduque). Y generar una nueva ficha 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 viejo usado usando una lista negra, pero necesitaría almacenar los tokens. Y uno de los beneficios de JWT es que no hay almacenamiento.
Encontré cómo Auth0 lo resolvió. Utilizan no solo el token JWT sino también un token de actualización: https://docs.auth0.com/refresh-token
Pero, de nuevo, para implementar esto (sin Auth0) necesitaría almacenar tokens de actualización y mantener su vencimiento. ¿Cuál es el beneficio real entonces? ¿Por qué no tener solo un token (no JWT) y mantener la caducidad en el servidor?
¿Hay otras opciones? ¿Usar JWT no es adecuado para este escenario?
Respuestas:
Trabajo en Auth0 y participé en el diseño de la función de token de actualización.
Todo depende del tipo de aplicación y aquí está 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 una 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 aplicación web aceptable UX.
Para actualizar el token, su API necesita un nuevo punto final que reciba un JWT válido, no caducado y que devuelva el mismo JWT firmado con el nuevo campo de caducidad. 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 solo una vez.
La idea es que el token de actualización nunca caduca y se puede intercambiar siempre 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, debe ser identificable por el usuario de alguna manera y la aplicación debe proporcionar una forma de revocar el acceso. Decidimos usar 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 es revocar el token de actualización en eventos específicos. Un evento interesante es cambiar la contraseña.
Creemos que JWT no es útil para estos casos de uso, por lo que utilizamos una cadena generada al azar y la almacenamos de nuestro lado.
fuente
En el caso de que maneje la autenticación usted mismo (es decir, no use un proveedor como Auth0), lo siguiente puede funcionar:
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 cuando el usuario inicia sesión la próxima vez.
Además, supongamos que tiene una política por 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 la API también verificará la última fecha de inicio de sesión del usuario desde la base de datos del usuario y denegará / permitirá la actualización del token sobre esa base.
fuente
Estaba jugando cuando movía nuestras aplicaciones a HTML5 con API RESTful en el back-end. La solución que se me ocurrió fue:
Como puede ver, esto reduce las frecuentes solicitudes de token de actualización. Si el usuario cierra el navegador / aplicación antes de que se active la llamada de renovación de 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, descuidó una pestaña de navegador abierta). En ese caso, la llamada de token de renovación debe incluir el tiempo de vencimiento esperado que no debe exceder el tiempo de sesión definido. La aplicación deberá realizar un seguimiento de la última interacción del usuario en consecuencia.
No me gusta la idea de establecer una caducidad larga, por lo tanto, este enfoque puede no funcionar bien con aplicaciones nativas que requieren autenticación menos frecuente.
fuente
Una solución alternativa para invalidar JWT, sin ningún almacenamiento seguro adicional en el backend, es implementar una nueva
jwt_version
columna entera en la tabla de usuarios. Si el usuario desea cerrar sesión o caducar los tokens existentes, simplemente incrementan eljwt_version
campo.Al generar un nuevo JWT, codifíquelo
jwt_version
en la carga útil de JWT, incrementando opcionalmente el valor de antemano si el nuevo JWT debe reemplazar a todos los demás.Al validar el JWT, el
jwt_version
campo se compara junto con eluser_id
y la autorización se otorga solo si coincide.fuente
Buena pregunta, y hay una gran cantidad de información en la pregunta misma.
El artículo Actualizar tokens: cuándo usarlos y cómo interactúan con JWT da una buena idea para este escenario. Algunos puntos son: -
También eche un vistazo a auth0 / angular-jwt angularjs
Para API web. lea Habilitar tokens de actualización de OAuth en la aplicación AngularJS utilizando ASP .NET Web API 2 y Owin
fuente
De hecho, implementé esto en PHP usando el cliente Guzzle para hacer una biblioteca de cliente para la API, pero el concepto debería funcionar para otras plataformas.
Básicamente, emito dos tokens, uno corto (5 minutos) y uno largo que expira después de una semana. La biblioteca del cliente usa middleware para intentar una actualización del token corto si recibe una respuesta 401 a alguna solicitud. Luego volverá a intentar la solicitud original y, si pudo actualizar, obtiene la respuesta correcta, de forma transparente para el usuario. Si falla, solo enviará el 401 al usuario.
Si el token corto ha caducado, pero sigue siendo auténtico y el token largo es válido y auténtico, actualizará el token corto utilizando un punto final especial en el servicio que el token largo autentica (esto es lo único para lo que se puede usar). Luego usará el token corto para obtener un nuevo token largo, extendiéndolo así una semana más cada vez que actualice el token corto.
Este enfoque también nos permite revocar el acceso en un máximo de 5 minutos, lo cual es aceptable para nuestro uso sin tener que almacenar una lista negra de tokens.
Edición tardía: volver a leer este mes después de que estaba fresco en mi cabeza, debo señalar que puede revocar el acceso al actualizar el token corto porque brinda la oportunidad de llamadas más caras (por ejemplo, llame a la base de datos para ver si el usuario ha sido prohibido) sin pagar por cada llamada a su servicio.
fuente
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 Actualizar tendrá un tiempo de vencimiento largo.
3) El Cliente (Front end) almacenará el token de actualización en su almacenamiento local y el token de acceso en las cookies.
4) El cliente usará token de acceso para llamar a apis. 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 y verificará su validez y devolverá un nuevo token de acceso.
6) Una vez que el token de actualización ha caducado, el usuario cerrará sesión.
Avíseme si necesita más detalles, también puedo compartir el código (arranque Java + Spring).
fuente
jwt-autorefresh
Si está utilizando el nodo (React / Redux / Universal JS) puede instalarlo
npm i -S jwt-autorefresh
.Esta biblioteca programa la actualización de tokens JWT a un número calculado por el usuario de segundos antes de que expire el token de acceso (según el reclamo de exp codificado en el token). Tiene un extenso conjunto de pruebas y verifica algunas condiciones para garantizar que cualquier actividad extraña vaya acompañada de un mensaje descriptivo sobre las configuraciones incorrectas de su entorno.
Implementación de ejemplo completo
descargo de responsabilidad: soy el mantenedor
fuente
jwt-autorefresh
descodifica es extraer elexp
reclamo para que pueda determinar qué tan lejos programar la próxima actualización.Resolví este problema agregando una variable en los datos del token:
Establezco la
expiresIn
opción en el tiempo deseado antes de que el usuario se vea obligado a iniciar sesión nuevamente. La mía se establece en 30 minutos. Esto debe ser mayor que el valor desoftexp
.Cuando mi aplicación del lado del cliente envía una solicitud a la API del servidor (donde se requiere token, por ejemplo, página de lista de clientes), el servidor verifica si el token enviado sigue siendo válido o no en función de su
expiresIn
valor de vencimiento ( ) original . Si no es válido, el servidor responderá con un estado particular para este error, por ejemplo.INVALID_TOKEN
.Si el token sigue siendo válido en función del
expiredIn
valor, pero ya excedió elsoftexp
valor, el servidor responderá con un estado separado para este error, por ejemplo.EXPIRED_TOKEN
:En el lado del cliente, si recibió
EXPIRED_TOKEN
respuesta, debería renovar el token automáticamente enviando una solicitud de renovación al servidor. Esto es transparente para el usuario y se ocupa automáticamente de la aplicación cliente.El método de renovación en el servidor debe verificar si el token sigue siendo válido:
El servidor se negará a renovar tokens si falló el método anterior.
fuente
¿Qué tal este enfoque:
No requerimos un punto final adicional para actualizar el token en este caso. Agradecería cualquier feedack.
fuente
Hoy en día, muchas personas optan por administrar las sesiones con JWT sin darse cuenta de lo que están renunciando por la simpleza percibida . Mi respuesta se desarrolla en la segunda parte de las preguntas:
Los JWT son capaces de admitir la gestión de sesión básica con algunas limitaciones. Al ser tokens autodescriptivos, no requieren ningún estado en el lado del servidor. Esto los hace atractivos. Por ejemplo, si el servicio no tiene una capa de persistencia, no necesita traer una solo para la administración de la sesión.
Sin embargo, la apatridia también es la causa principal de sus deficiencias. Dado que solo se emiten una vez con contenido fijo y caducidad, no puede hacer las cosas que le gustaría con una configuración de administración de sesión típica.
Es decir, no puede invalidarlos a pedido. Esto significa que no puede implementar un cierre de sesión seguro ya que no hay forma de expirar los tokens ya emitidos. Tampoco puede implementar el tiempo de espera inactivo por el mismo motivo. Una solución es mantener una lista negra, pero eso introduce el estado.
Escribí una publicación explicando estos inconvenientes con más detalle. Para que quede claro, puede solucionarlos agregando más complejidad (sesiones deslizantes, tokens de actualización, etc.)
En cuanto a otras opciones, si sus clientes solo interactúan con su servicio a través de un navegador, le recomiendo utilizar una solución de administración de sesión basada en cookies. También compilé una lista de métodos de autenticación actualmente ampliamente utilizados en la web.
fuente