Manejo de renovación de token / caducidad de sesión en una API RESTful

17

Estoy construyendo una API RESTful que usa tokens JWT para la autenticación de usuarios (emitida por un loginpunto final y enviada en todos los encabezados después), y los tokens deben actualizarse después de un período de tiempo fijo (invocando un renewpunto final, que devuelve un token renovado )

Es posible que la sesión de API de un usuario se vuelva inválida antes de que caduque el token, por lo tanto, todos mis puntos finales comienzan verificando que: 1) el token sigue siendo válido y 2) la sesión del usuario sigue siendo válida. No hay forma de invalidar directamente el token, porque los clientes lo almacenan localmente.

Por lo tanto, todos mis puntos finales deben indicar a mis clientes dos posibles condiciones: 1) que es hora de renovar el token o 2) que la sesión se ha vuelto inválida y ya no se les permite acceder al sistema. Puedo pensar en dos alternativas para que mis puntos finales indiquen a sus clientes cuando se produce una de las dos condiciones (supongamos que los clientes pueden adaptarse a cualquiera de las opciones):

  1. Devuelve un código http 401 (no autorizado) si la sesión se ha vuelto inválida o devuelve un código 412 (condición previa fallida) cuando el token ha expirado y es hora de llamar al renewpunto final, que devolverá un código 200 (ok).
  2. Devuelva 401 para indicar que la sesión no es válida o que el token ha expirado. En este caso, el cliente llamará inmediatamente al renewpunto final, si devuelve 200, el token se actualiza, pero si renewtambién devuelve 401, significa que el cliente está fuera del sistema.

¿Cuál de las dos alternativas anteriores recomendaría? ¿Cuál sería más estándar, más simple de entender y / o más RESTANTE? ¿O recomendarías un enfoque completamente diferente? ¿Ve algún problema obvio o riesgos de seguridad con cualquiera de las opciones? Puntos adicionales si su respuesta incluye referencias externas que respaldan su opinión.

ACTUALIZAR

Chicos, concéntrense en la pregunta real: ¿ cuál de las dos alternativas de código http para señalar una renovación / invalidación de sesión es la mejor? No importa el hecho de que mi sistema usa JWT y sesiones del lado del servidor, esa es una peculiaridad de mi API para reglas comerciales muy específicas, y no la parte para la que busco ayuda;)

Óscar López
fuente
¿Cómo se invalidaría la sesión de un usuario antes de que caduque el token, suponiendo un tiempo de caducidad corto (aproximadamente 5 minutos)?
Jack
Debido a una regla de negocios, una parte diferente del sistema podría invalidar la sesión.
Óscar López
1
Los JWT son para prueba de identidad, ya que en "esta solicitud se demuestra que es del usuario X". Si su regla empresarial es algo así como "el usuario X ya no puede interactuar con el recurso Y", eso es algo que debe verificarse por separado del JWT.
Jack
@Jack exactamente! ese es precisamente mi punto y la razón por la que tengo que usar una capa adicional con estado para guardar la información de la sesión. El JWT simple, por agradable que sea, simplemente no está hecho para el trabajo.
Óscar López
1
Quizás te interese mi respuesta entonces :)
Jack

Respuestas:

22

Esto suena como un caso de autenticación versus autorización .

Los JWT son reclamos firmados criptográficamente sobre el creador de una solicitud. Un JWT puede contener notificaciones como "Esta solicitud es para el usuario X" y "El usuario X tiene roles de administrador". Obtener y proporcionar esta prueba a través de contraseñas, firmas y TLS es el dominio de la autenticación , lo que demuestra que usted es quien dice ser.

Lo que esas afirmaciones significan para su servidor (lo que los usuarios y roles específicos pueden hacer) es el problema de la autorización . La diferencia entre los dos se puede describir con dos escenarios. Supongamos que Bob quiere ingresar a la sección de almacenamiento restringido del almacén de su empresa, pero primero debe tratar con un guardia llamado Jim.

Escenario A - Autenticación

  • Bob: "Hola Jim, me gustaría ingresar al almacenamiento restringido".
  • Jim: "¿Tienes tu placa?"
  • Bob: "No, lo olvidé".
  • Jim: "Lo siento amigo, no hay entrada sin una insignia".

Escenario B - Autorización

  • Bob: "Hola Jim, me gustaría ingresar al almacenamiento restringido. Aquí está mi credencial".
  • Jim: "Hola Bob, necesitas autorización de nivel 2 para entrar aquí. Lo siento".

Los tiempos de vencimiento de JWT son un dispositivo de autenticación utilizado para evitar que otros los roben. Si todos sus JWT tienen tiempos de vencimiento de cinco minutos, no es tan importante si se los roban porque rápidamente se volverán inútiles. Sin embargo, la regla de "caducidad de la sesión" que discute suena como un problema de autorización. Algún cambio en el estado significa que el usuario X ya no puede hacer algo que solía hacer. Por ejemplo, el usuario Bob podría haber sido despedido; no importa que su credencial diga que ya es Bob, porque el simple hecho de ser Bob ya no le otorga ninguna autoridad con la compañía.

Estos dos casos tienen códigos de respuesta HTTP distintos: 401 Unauthorizedy 403 Forbidden. El código 401, lamentablemente llamado, es para problemas de autenticación tales como credenciales faltantes, caducadas o revocadas. 403 es para autorización, donde el servidor sabe exactamente quién es usted, pero simplemente no tiene permitido hacer lo que intenta hacer. En el caso de que se elimine la cuenta de un usuario, intentar hacer algo con un JWT en un punto final daría como resultado una respuesta 403 Prohibida. Sin embargo, si el JWT caduca, el resultado correcto sería 401 no autorizado.

Un patrón JWT común es tener tokens "de larga duración" y "de corta duración". Los tokens de larga duración se almacenan en el cliente como tokens de corta duración, pero tienen un alcance limitado y solo se usan con su sistema de autorización para obtener tokens de corta duración. Los tokens de larga duración, como su nombre lo indica, tienen períodos de vencimiento muy largos; puede usarlos para solicitar nuevos tokens durante días o semanas. Los tokens de corta duración son los que estás describiendo, utilizados con tiempos de vencimiento muy cortos para interactuar con tu sistema. Los tokens de larga duración son útiles para implementar la funcionalidad Remember Me, por lo que no necesita proporcionar su contraseña cada cinco minutos para obtener un nuevo token de corta duración.

El problema de "invalidación de sesión" que está describiendo suena similar a intentar invalidar un JWT de larga duración, ya que los de corta duración rara vez se almacenan en el lado del servidor, mientras que los de larga duración se rastrean en caso de que necesiten ser revocados. En dicho sistema, intentar adquirir credenciales con un token revocado de larga duración daría como resultado 401 no autorizado, porque el usuario técnicamente podría ser capaz de adquirir credenciales, pero el token que están utilizando no es adecuado para la tarea. Luego, cuando el usuario intenta adquirir un nuevo token de larga duración utilizando su nombre de usuario y contraseña, el sistema podría responder con 403 Prohibido si es expulsado del sistema.

Jack
fuente
3
Esta es la guía que estaba buscando, y usted aportó una visión muy relevante a la discusión: que este es un caso de autenticación frente a autorización, y cada uno debe tratarse de manera diferente. ¡Gracias!
Óscar López
16

Su sesión de API es algo que no debería existir en un mundo RESTful en absoluto. Se supone que las operaciones RESTful no tienen estado, la sesión contiene estado y, por lo tanto, no tiene lugar en un mundo RESTful.

El JWT debería ser su única forma de determinar si un usuario aún es elegible para acceder a un punto final o no. Una sesión no debe desempeñar absolutamente ningún papel en ella. Si es así, no tiene una API RESTful.

Cuando elimina la sesión por completo, lo que si está buscando una API RESTful que debe hacer, y solo usa el JWT como factor de autenticación, un usuario está autorizado a usar su punto final o no, en cuyo caso el 401 Unauthorizedcódigo de respuesta es apropiado - y debe llamar al renewpunto final con grant_type=refresh_tokeno cualquier identificación de renovación que esté utilizando.

Actualizar:

Según el comentario, parece que el flujo de validación del JWT que está utilizando actualmente no es correcto. Se supone que la validación se verá así:

  Client        RESTful API      JWT Issuer
     |              |                |
     |----- 1. ---->|                | 
     |              |------ 2. ----->|-----
     |              |                | 3. |
     |              |<----- 4. ------|<----
-----|<---- 5. -----|                |
| 6. |              |                |
---->|----- 7. ---->|                |
     |              |------ 8. ----->|-----
     |              |                | 9. |
     |              |<----- 10. -----|<----
     |              |                |
     |              |------          |
     |              | 11. |          |
     |<---- 12. ----|<-----          |
     |              |                |
     .              .                .

1. Ask RESTful API for a JWT using login endpoint.
2. Ask Issuer to create a new JWT.
3. Create JWT.
4. Return JWT to the RESTful API.
5. Return JWT to Client.
6. Store JWT to append it to all future API requests.
7. Ask for data from API providing JWT as authorization.
8. Send JWT to Issuer for verification.
9. Issuer verifies JWT.
10. Issuer returns 200 OK, verification successful.
11. Retrieve and format data for Client.
12. Return data to Client.

El servidor RESTful APItiene que verificar la validez del token que se envía como Autorización. Esa no es responsabilidad del Client. Parece que actualmente no estás haciendo esto. Implemente la verificación del JWT de esta manera y no necesitará sesiones en absoluto.

Andy
fuente
Gracias por tu respuesta. De acuerdo, usar una sesión no sería un enfoque 100% RESTful, pero como mencioné anteriormente, necesito negar el acceso a algunos usuarios antes de que caduque el token.
Óscar López
2
@ ÓscarLópez Entonces simplemente invalide los tokens que los usuarios están usando. Ya no podrán acceder a la API utilizando el token proporcionado (que ahora habrá sido invalidado) y no necesita una sesión.
Andy
1
Los tokens se almacenan en el cliente, ¿cómo puedo invalidarlos allí? Tendría que hacer un seguimiento de cuáles son válidos ... y ahí es donde el estado levanta su fea cabeza. A veces es inevitable.
Óscar López
Un cliente malintencionado podría seguir enviando un token previamente válido mientras su tiempo de vencimiento lo permita, pero necesito expulsarlo del sistema de inmediato, por lo tanto, establecer un tiempo de renovación breve tampoco es una opción.
Óscar López
44
@Laiv Hice el diagrama de secuencia en el bloc de notas.
Andy
1

Por lo tanto, confesaré que no tiene mucho sentido para mí preocuparme sobre qué enfoque es más RESTANTE cuando ya está rompiendo las convenciones REST con la sesión, pero entiendo que satisface sus requisitos comerciales.

Desde el punto de vista REST, el cliente está autenticado o no. A la arquitectura no le importa mucho por qué (eso es inyectar un estado innecesario), por lo que para responder a su pregunta principal, no tendría un punto final renovado. Un cliente conectado siempre enviará su JWT y el servidor siempre lo validará y lo aceptará enviando el código de éxito apropiado basado en la acción 200, 201, etc.) o lo rechazará con un 401 o 403 según corresponda.

Ahora, el JWT se asociará con una cuenta de algún tipo. Esa cuenta puede estar bloqueada o restringida o lo que sea, por lo que el token en sí puede ser válido, pero la acción aún se rechazará en otro lugar. Si el caso es que la cuenta de usuario está bloqueada debido a las reglas comerciales, entonces sigue siendo un 401 o 403, dependiendo de la cantidad de información que desee proporcionar al cliente (las diferentes empresas tienen diferentes opiniones al respecto).

Finalmente, si está afirmando que la cuenta podría estar desbloqueada y ser válida pero el JWT solo necesita ser revocado, TODAVÍA me quedaría con el 401 o 403 y mantendría algo así como una Lista de revocación de certificados de JWT inválidos en los que puede poner uno , siempre y cuando se limpie cuando JWT haya expirado (la mayoría de las bases de datos tienen una manera de hacerlo o puede tener eventos en el código de la aplicación).

Pablo
fuente
Se supone que son apátridas. en el momento en que cuestionas su contenido y lo validas contra un db, ya no es apátrida, por lo que se vuelve redundante, ya que hay mejores soluciones para las sesiones con estado
Stavm