JWT (JSON Web Token) prolongation automatique de l'expiration

j'aimerais implémenter l'authentification basée sur JWT à notre nouvelle API REST. Mais puisque l'expiration est définie dans le token, est-il possible de la prolonger automatiquement? Je ne veux pas que les utilisateurs doivent s'inscrire après chaque X minutes s'ils utilisaient activement l'application dans cette période. Ce serait un énorme échec UX.

mais prolonger l'expiration crée un nouveau token (et l'ancien est toujours valide jusqu'à ce qu'il expire). Et générer un nouveau token après chaque la demande est ridicule pour moi. Sonne comme un problème de sécurité lorsque plus d'un jeton est valide en même temps. Bien sûr, je pourrais invalider le vieux utilisé un en utilisant une liste noire, mais je devrais stocker les jetons. Et L'un des avantages de JWT est pas de stockage.

J'ai trouvé comment Auth0 l'a résolu. Ils utilisent non seulement JWT token mais aussi un token de rafraîchissement: https://docs.auth0.com/refresh-token

mais encore une fois, pour mettre en œuvre ce (sans Auth0) j'aurais besoin de stocker des jetons de rafraîchissement et de maintenir leur expiration. Quel est le bénéfice réel alors? Pourquoi ne pas avoir un seul token (pas JWT) et garder l'expiration sur le serveur?

y a-t-il d'autres options? L'utilisation de JWT n'est-elle pas adaptée à ce scénario?

379
demandé sur cchamberlain 2014-11-04 18:41:22

9 réponses

je travaille chez Auth0 et j'ai participé à la conception de la fonctionnalité de rafraîchissement.

tout dépend du type d'application et voici notre approche recommandée.

applications Web

un bon motif est de rafraîchir le token avant qu'il n'expire.

réglez l'expiration du token à une semaine et rafraîchissez Le token chaque fois que l'utilisateur ouvre l'application web et toutes les heures. Si un utilisateur n'a pas ouvrez l'application pendant plus d'une semaine, ils devront se connecter à nouveau et C'est acceptable application Web UX.

pour rafraîchir le jeton, votre API a besoin d'un nouveau terminal qui reçoit un JWT valide, non expiré et renvoie le même JWT signé avec le nouveau champ d'expiration. Alors l'application web stockera le token quelque part.

Mobile/applications Natives

la plupart des applications natives se connectent une seule fois.

l'idée est que le jeton de rafraîchissement n'expire jamais et il peut être échangé toujours contre un JWT valide.

le problème avec un jeton qui n'expire jamais est que jamais signifie Jamais. Que faites-vous si vous perdez votre téléphone? Donc, il doit être identifiable par l'utilisateur en quelque sorte et l'application doit fournir un moyen de révoquer les droits d'accès. Nous avons décidé d'utiliser le nom de l'appareil, par exemple "iPad de maryo". Ensuite, l'utilisateur peut aller à l' application et révoquer l'accès à "maryo de l'iPad".

une autre approche consiste à révoquer le jeton de rafraîchissement sur des événements spécifiques. Un événement intéressant est de changer le mot de passe.

nous croyons que JWT n'est pas utile pour ces cas d'utilisation donc nous utilisons une chaîne générée au hasard et nous la stockons de notre côté.

437
répondu José F. Romaniello 2016-11-04 16:22:55

dans le cas où vous manipulez l'Autorité vous-même (I. e n'utilisez pas un fournisseur comme Auth0), ce qui suit peut fonctionner:

  1. émettent JWT token avec une échéance relativement courte, disons 15min.
  2. L'Application
  3. vérifie la date d'expiration du jeton avant toute transaction nécessitant un jeton (le jeton contient la date d'expiration). Si le token est expiré, alors il demande D'abord à L'API de 'rafraîchir' le token (ceci est fait de manière transparente au UX).
  4. API gets requête de rafraîchissement de token, mais vérifie d'abord la base de données de l'utilisateur pour voir si un drapeau 'reauth' a été placé par rapport à ce profil d'utilisateur (le token peut contenir l'identifiant de l'utilisateur). Si le drapeau est présent, alors le jeton d'actualisation est refusé, sinon, un nouveau jeton est émis.
  5. je répète.

le drapeau "reauth" dans le backend de la base de données serait défini lorsque, par exemple, l'Utilisateur a réinitialisé son mot de passe. Le drapeau est enlevé lorsque l'utilisateur se connecte la prochaine fois.

en outre, disons que vous avez une politique par laquelle un utilisateur doit se connecter au moins une fois toutes les 72 heures. Dans ce cas, la logique de rafraîchissement de votre token API vérifierait également la dernière date de connexion de l'utilisateur à partir de la base de données de l'utilisateur et nierait/autoriserait la rafraîchissement du token sur cette base.

56
répondu IanB 2016-01-19 00:12:15

j'étais en train de bricoler en déplaçant nos applications vers HTML5 avec des API reposantes dans le backend. La solution que j'ai trouvée était:

Le Client
  1. reçoit un token avec un temps de session de 30 minutes (ou quel que soit le temps de session habituel côté serveur) après une connexion réussie.
  2. un minuteur côté client est créé pour appeler un service afin de renouveler le jeton avant son expiration. Le nouveau jeton remplacera le jeton existant dans les prochains appels.

comme vous pouvez le voir, cela réduit les requêtes de rafraîchissement fréquentes. Si l'utilisateur ferme le navigateur/l'application avant que l'appel de token ne soit déclenché, le token précédent expirera dans le temps et l'Utilisateur devra se reconnecter.

une stratégie plus compliquée peut être mise en œuvre pour répondre à l'inactivité de l'utilisateur (par exemple négligé un onglet navigateur ouvert). Dans ce cas, le renouvellement de jeton d'appel devrait comprendre les attendus expirant le temps qui ne doit pas dépasser la temps de session défini. L'application devra tenir compte de la dernière interaction utilisateur en conséquence.

Je n'aime pas l'idée de définir l'expiration longue donc cette approche peut ne pas bien fonctionner avec des applications natives nécessitant une authentification moins fréquente.

13
répondu coolersport 2015-05-21 03:00:16

une solution alternative pour invalider JWTs, sans aucun stockage sécurisé supplémentaire sur le backend, est d'implémenter une nouvelle colonne entière jwt_version sur la table des utilisateurs. Si l'Utilisateur souhaite déconnecter ou expirer des tokens existants, il n'a qu'à incrémenter le champ jwt_version .

lors de la génération d'une nouvelle JWT, encodez le jwt_version dans la charge utile JWT, en augmentant éventuellement la valeur à l'avance si la nouvelle JWT doit remplacer toutes les autres.

lors de la validation de la JWT, le champ jwt_version est comparé avec le champ user_id et l'autorisation n'est accordée que si elle correspond.

11
répondu Ollie Bennett 2017-05-31 09:19:31

bonne question - et il ya richesse d'informations dans la question elle-même.

L'article Actualisation des Jetons: Quand les Utiliser et Comment Ils Interagissent avec JWTs donne une bonne idée de ce scénario. Quelques points sont: -

  • les jetons de rafraîchissement contiennent les informations nécessaires pour obtenir un nouvel accès jeton.
  • Les tokens de rafraîchissement
  • peuvent aussi expirer mais sont plutôt de longue durée.
  • Les tokens de rafraîchissement sont généralement soumis à des exigences de stockage strictes pour s'assurer qu'ils ne sont pas divulgués.
  • ils peuvent aussi être mis sur liste noire par le serveur d'autorisation.

Aussi jeter un oeil à auth0/angulaires-jwt angularjs

pour L'API Web. read Enable OAuth Refresh Tokens in AngularJS App using ASP .NET Web API 2, and Owin

7
répondu Lijo 2016-08-26 17:50:23

j'ai en fait implémenté ceci en PHP en utilisant le client Guzzle pour créer une bibliothèque client pour l'api, mais le concept devrait fonctionner pour d'autres plateformes.

en gros, j'émets deux jetons, un court (5 minutes) un et un long qui expire après une semaine. La bibliothèque du client utilise middleware pour essayer un rafraîchissement du token court si elle reçoit une réponse 401 à une requête. Il va alors essayer la requête originale de nouveau et s'il a pu rafraîchir obtient le réponse correcte, transparente à l'utilisateur. S'il échoue, il enverra la 401 à l'utilisateur.

si le jeton court est expiré, mais qu'il est toujours authentique et que le jeton long est valide et authentique, il rafraîchira le jeton court en utilisant un endpoint spécial sur le service que le jeton long authentifie (c'est la seule chose pour laquelle il peut être utilisé). Il utilisera alors le jeton court pour obtenir un nouveau jeton long, l'étendant ainsi une autre semaine chaque fois qu'il rafraîchit le court jeton.

cette approche nous permet également de révoquer l'accès dans un délai maximum de 5 minutes, ce qui est acceptable pour notre utilisation sans avoir à stocker une liste noire de jetons.

Fin edit: Re-lecture de ce mois après il était encore fraîche dans ma tête, je me dois de souligner que vous pouvez révoquer l'accès lors de l'actualisation du court jeton car il donne l'occasion pour plus d'appels coûteux (par exemple, appel à la base de données pour voir si l'utilisateur a été banni) sans payer pour chaque appel à votre service.

6
répondu BytePorter 2017-05-22 14:54:58

jwt-actualisation automatique

si vous utilisez node (React / Redux / Universal JS), vous pouvez installer npm i -S jwt-autorefresh .

cette bibliothèque programme la mise à jour des tokens JWT à un nombre calculé par l'utilisateur de secondes avant l'expiration du token d'accès (basé sur la réclamation exp encodée dans le token). Il dispose d'une vaste suite de tests et vérifie un certain nombre de conditions pour s'assurer que toute activité étrange est accompagnée d'un descriptif message concernant les erreurs de configuration de votre environnement.

exemple Complet de mise en œuvre

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())

clause de non-responsabilité: je suis le responsable

5
répondu cchamberlain 2016-10-05 13:42:09

Que pensez-vous de cette approche:

  • pour chaque requête client, le serveur compare le temps d'expiration du jeton avec (currentTime - lataccesstime)
  • si expirationTime < (currentTime - lataccessedtime) , il change la dernière date d'expiration en heure actuelle.
  • en cas d'inactivité sur le navigateur pour une durée excédant le délai d'expiration ou dans le cas où la fenêtre du navigateur a été fermé et le expirationTime > (currentTime - lataccessedtime) , puis le serveur peut expirer le token et demander à l'utilisateur de se connecter à nouveau.

nous n'avons pas besoin de point final supplémentaire pour rafraîchir le jeton dans ce cas. J'apprécierais tout feedack.

2
répondu sjaiswal 2016-05-10 21:31:08

j'ai résolu ce problème en ajoutant une variable dans les données token:

softexp - I set this to 5 mins (300 seconds)

j'ai placé expiresIn option à l'heure désirée avant que l'utilisateur ne soit forcé de se connecter à nouveau. Le mien est réglé à 30 minutes. Ce doit être supérieure à la valeur de softexp .

lorsque mon application côté client envoie une requête à l'API du serveur (où un token est requis, par ex. liste des clients), le serveur vérifie si le token soumis est toujours valide ou non sur la base de sa valeur d'expiration originale ( expiresIn ). Si elle n'est pas valide, le serveur répondra avec un statut particulier pour cette erreur, par exemple. INVALID_TOKEN .

si le token est toujours valide sur la base de la valeur expiredIn , mais qu'il a déjà dépassé la valeur softexp , le serveur répondra avec un statut séparé pour cette erreur, par exemple. EXPIRED_TOKEN :

(Math.floor(Date.now() / 1000) > decoded.softexp)

du côté du client, si elle a reçu la réponse EXPIRED_TOKEN , elle devrait renouveler la token automatiquement en envoyant une demande de renouvellement au serveur. C'est transparent pour l'utilisateur et automatiquement la prise en charge de l'application cliente.

la méthode de renouvellement dans le serveur doit vérifier si le token est toujours valide:

jwt.verify(token, secret, (err, decoded) => {})

le serveur refusera de renouveler les tokens s'il échoue à la méthode ci-dessus.

1
répondu James A 2017-08-04 21:29:46