web-dev-qa-db-fra.com

Gestion de la fonctionnalité d'expiration / "se souvenir de moi" avec JWT

Conceptuellement, j'aime vraiment JWT car il est conforme à l'apatridie de REST etc (aucun état enregistré côté serveur, toutes les données pertinentes sont contenues dans le jeton).

Ce dont je ne suis pas sûr: comment géreriez-vous l'expiration des jetons lorsque vous n'êtes pas connecté (c'est-à-dire une fonctionnalité "se souvenir de moi")?

Il y a une couverture émergente de JWT sur le Web, mais je n'ai encore trouvé personne qui ait répondu à la question d'expiration.

Clarification: Je ne demande pas comment gérer un jeton qui expirera bientôt, mais que faire quand un jeton a déjà expiré (utilisateur a fermé le site Web/l'application pendant un certain temps). La solution la plus simple qui me vient à l'esprit est la mise en cache des informations d'identification de l'utilisateur, ce qui est plutôt peu sûr.

46
arnuschky

Je ne suis pas si sûr de suivre mais j'écrirai ce que je pense.

Imaginez le jeton comme une carte d'hôtel, vous payez à l'avance pendant 5 jours (rappelez-vous que j'expire 5 jours). Je peux entrer dans le bâtiment, le garage, la chambre, etc. dans ces 5 jours, après ces 5 jours, cela ne fonctionnera plus.

Que faire lorsque le jeton a déjà expiré? Rien du tout.

Imaginez que je paie ces 5 jours et meh, j'ai eu une urgence et je rentre chez moi (avec la carte sur la poche). L'hôtel ne s'en soucie pas du tout, quand les 5 jours passent, la carte est juste un morceau de plastique inutile et si vous essayez de l'utiliser sur l'hôtel, cela ne fera rien.

Revenons donc au développement Web. Si vous proposez un service Remember me, vous pouvez fixer une date d'expiration à 7 jours. Tant que l'utilisateur dispose du token, il peut accéder au service sans aucun problème. S'il perd le jeton, il doit se reconnecter. S'il utilise le jeton et qu'il a expiré, il devra également se reconnecter.

S'il se connecte, il obtient un jeton pendant 7 jours, s'il ne l'utilise plus et après 20 jours, il revient, il devra se reconnecter, le serveur refusera simplement vos pétitions jusqu'à ce que vous le fassiez.

Ce que je ferais si vous utilisez quelque chose comme angular sur le frontend est de vérifier la validation du jeton au démarrage afin que vous puissiez avoir une expérience utilisateur agréable.

Ce que je ne comprends pas dans votre question, c'est la mise en cache.

8
Jesus Rodriguez

En plus de @ Jesus answer , vous pouvez penser à implémenter un système de jeton de rafraîchissement: https://auth0.com/blog/refresh-tokens-what-are-they-and- quand les utiliser /

Dans l'exemple d'hôtel, votre carte d'hôtel (jeton d'accès) serait invalide après l'heure X, mais à la réception, vous pouvez utiliser votre passeport (jeton de rafraîchissement) pour obtenir à nouveau une nouvelle carte d'hôtel.

Vous pouvez stocker le jeton d'actualisation dans la base de données avec des données supplémentaires sur l'appareil que l'utilisateur utilise, lui permettant de désactiver l'appareil en cas de vol.

Exemple:

  1. première connexion client correcte: créez un jeton d'actualisation qui est valide pour toujours (jusqu'à ce qu'il soit supprimé ou invalidé)
  2. stocker le jeton d'actualisation dans la base de données
  3. renvoyer le jeton d'accès (JWT) avec l'heure d'expiration au client (ce jeton n'est pas stocké dans la base de données)
  4. pour la prochaine requête, le client envoie le jeton d'accès

  5. Vérifiez maintenant si le jeton d'accès a expiré:

    5.1 Jeton d'accès non expiré, tout va bien

    5.2 Jeton d'accès expiré, vérifiez s'il y a un jeton d'actualisation dans la base de données

    5.2.1 Le jeton d'actualisation est dans la base de données, retourne un nouveau jeton d'accès

    5.2.2 Aucun jeton d'actualisation dans la base de données, retourne 401/déconnexion, l'utilisateur doit se reconnecter

J'espère que cela t'aides.

5
Felix Hagspiel

Vous devez conserver le JWT sur le client afin qu'il soit disponible à travers les chargements de page, la stratégie la plus sécurisée est un cookie HTTPS uniquement. Cela enverra le JWT à votre serveur à chaque demande et le serveur peut vérifier la validité du jeton et le rejeter s'il est expiré. La façon dont vous gérez l'expiration dépend du type d'application Web dont vous disposez.

Pour une application d'une seule page (par exemple, les applications Angular.js), vous souhaitez structurer l'application afin qu'elle fasse une demande initiale au serveur avant de démarrer le reste de l'application. Si le serveur constate que le JWT dans cette demande a expiré, il émettra une réponse 401. Votre application répondrait à cette réponse en affichant un formulaire de connexion. Sinon, il continuerait avec l'hypothèse que le JWT est valide et peut être utilisé pour accéder aux ressources requises. Si, à tout moment, l'application voit un 401, elle devrait ramener l'utilisateur au formulaire de connexion.

Pour les applications Web traditionnelles qui affichent leurs pages sur le serveur: pour toute demande dont le JWT a expiré (tel que lu à partir du cookie), le serveur doit émettre une redirection 302 vers un formulaire de connexion.

3
robertjd

Je peux penser à une façon, mais ce n'est pas vraiment défini la norme.

Qu'en est-il de l'ajout d'un autre type de date d'expiration avec une durée de vie différente aux revendications? Avec deux revendications, nous pouvons traiter la plus courte comme la date d'expiration de l'accès aux ressources et la plus longue comme la date d'expiration de l'actualisation, par ex.

{
    "iat": /* current time */,
    "bbf": /* current time + 1 hour -- expired means no resource access */
    "exp": /* current time + 1 week -- expired means cannot refresh */
}

(Remarque: j'utilise bbf pour la date d'expiration la plus courte. Aucune raison spécifique, juste parce qu'il contient 3 caractères.)

Ainsi, avec l'option "Se souvenir de moi" cochée, lorsque l'utilisateur se reconnecte, il peut utiliser le même jeton pour en demander un nouveau, mais pas pour accéder à la ressource. Avec cela, toutes les données pertinentes sont contenues dans le jeton - aucun jeton supplémentaire requis.

Et enfin, lorsque "se souvenir de moi" n'est pas coché, utilisez simplement la même durée de vie pour bbf et exp.

1
hiapay

Je pense que ce que vous demandez, c'est comment invalider un côté serveur JWT pour les jetons à longue expiration (par exemple, la fonctionnalité "se souvenir de moi")?

J'ai rencontré ce problème moi-même récemment et j'ai fini par utiliser un secret d'utilisateur unique pour invalider le jeton, lorsque l'utilisateur tente de valider un jeton produit avec un ancien secret, il échouera. Le nom d'utilisateur se trouve dans la pré-vérification JWT décodée.

Vous pouvez probablement même utiliser le sel de mot de passe des utilisateurs pour cela, de cette façon, tout JWT actuel serait invalidé lorsqu'un utilisateur change de mot de passe (en supposant que vous modifiez également le sel en même temps), cela peut être problématique, car le hachage du mot de passe et les JWT deviendrait étroitement couplé

1
Daniel Landers

Il n'y a pas de réponse en noir et blanc au problème donné de la façon d'implémenter la fonctionnalité Remember-me sur le plan théorique. Beaucoup d'arguments théoriques sont donnés sur les raisons pour lesquelles quelque chose ne devrait pas être fait, alors que dans le même temps, aucune réponse claire n'est donnée au problème quant à la façon dont cela doit être fait dans la pratique.

Souvenez-vous que le problème vient implicitement du fait que vous avez besoin d'une fenêtre d'expiration de jeton plus longue, il n'y a tout simplement pas de solution. Le moyen le plus sûr consiste à faire en sorte que l'utilisateur se connecte régulièrement à une brève expiration; cependant, personne n'aime vraiment cela, donc un compromis est fait où la perfection théorique sécurisée est mise en balance avec des mesures pratiques.

La façon dont ce compromis fonctionne consiste à corriger les imperfections qui accompagnent une longue expiration pour un jeton. Mais ne vous y trompez pas, vous aurez besoin d'un jwt/cookie avec une longue expiration (que vous utilisiez deux jetons, implémentez un mécanisme de rafraîchissement secondaire ou autre, à la fin vous rencontrez le même problème)!
Sur la base d'articles que j'ai lus sur la façon dont les autres l'ont fait, voici comment cela se fait.

La façon dont je vais implémenter cela est d'offrir une expiration de 3 mois sur un jwt stocké dans un cookie httpOnly/secure lorsque l'utilisateur vérifie se souvenir de moi.
Lors de la déconnexion, effacez simplement le cookie.
Évidemment, protégez en utilisant les mesures https/CSRF en haut.

Si vous n'êtes pas d'accord, arrêtez de vous plaindre et offrez une solution alternative - ce qui n'est clairement pas le cas du nombre infini de discussions que j'ai lues à ce sujet.
S'il y avait une solution simple au problème, il n'y aurait probablement pas autant de discussions à ce sujet en premier lieu.

0
Trace