node.js - token parser




JWT(JSON Web Token) prolungamento automatico della scadenza (7)

jwt-autorefresh

Se si utilizza il nodo (React / Redux / Universal JS) è possibile installare npm i -S jwt-autorefresh .

Questa libreria pianifica l'aggiornamento dei token JWT in base a un numero calcolato di secondi dall'utente prima della scadenza del token di accesso (in base al reclamo exp codificato nel token). Ha una vasta suite di test e verifica alcune condizioni per garantire che qualsiasi strana attività sia accompagnata da un messaggio descrittivo relativo alle configurazioni errate del tuo ambiente.

Implementazione completa di esempio

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

disclaimer: sono il manutentore

Vorrei implementare l'autenticazione basata su JWT nella nostra nuova API REST. Ma poiché la scadenza è impostata nel token, è possibile prolungarla automaticamente? Non voglio che gli utenti debbano accedere dopo ogni X minuti se stavano attivamente utilizzando l'applicazione in quel periodo. Sarebbe un enorme fallimento della UX.

Ma il prolungamento della scadenza crea un nuovo token (e quello vecchio è ancora valido fino alla scadenza). E generare un nuovo token dopo ogni richiesta mi sembra sciocco. Sembra un problema di sicurezza quando più di un token è valido contemporaneamente. Naturalmente potrei invalidare il vecchio usato usando una lista nera ma avrei bisogno di conservare i token. E uno dei vantaggi di JWT è l'archiviazione.

Ho trovato come Auth0 ha risolto il problema. Usano non solo il token JWT ma anche un token di aggiornamento: https://docs.auth0.com/refresh-token

Ma ancora una volta, per implementare questo (senza Auth0) avrei bisogno di memorizzare i token di aggiornamento e mantenere la loro scadenza. Qual è il vero vantaggio quindi? Perché non avere un solo token (non JWT) e mantenere la scadenza sul server?

Ci sono altre opzioni? L'uso di JWT non è adatto a questo scenario?


Buona domanda - e ci sono molte informazioni nella domanda stessa.

L'articolo Aggiorna token: quando usarli e come interagiscono con i JWT offre una buona idea per questo scenario. Alcuni punti sono: -

  • I token di aggiornamento contengono le informazioni necessarie per ottenere un nuovo token di accesso.
  • I token di aggiornamento possono anche scadere ma sono piuttosto longevi.
  • I token di aggiornamento sono generalmente soggetti a rigidi requisiti di archiviazione per garantire che non vi siano perdite.
  • Possono anche essere inseriti nella blacklist dal server di autorizzazione.

auth0/angular-jwt anche un'occhiata a auth0/angular-jwt angularjs

Per l'API Web. leggi Abilita token di aggiornamento OAuth nell'app AngularJS usando l'API Web ASP .NET 2 e Owin


Di seguito sono riportati i passaggi per revocare il token di accesso JWT:

1) Quando si effettua l'accesso, inviare 2 token (token di accesso, token di aggiornamento) in risposta al client.
2) Il token di accesso avrà meno tempo di scadenza e Refresh avrà un lungo tempo di scadenza.
3) Il client (front-end) memorizzerà il token di aggiornamento nella sua memoria locale e accederà al token nei cookie.
4) Il client utilizzerà il token di accesso per chiamare le API. Ma quando scade, seleziona il token di aggiornamento dalla memoria locale e chiama l'API del server di autenticazione per ottenere il nuovo token.
5) Il server di autenticazione avrà un'API esposta che accetterà il token di aggiornamento e ne verificherà la validità e restituirà un nuovo token di accesso.
6) Una volta scaduto il token di aggiornamento, l'utente verrà disconnesso.

Per favore fatemi sapere se avete bisogno di maggiori dettagli, posso condividere anche il codice (Java + Spring boot).


Ho risolto questo problema aggiungendo una variabile nei dati token:

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

Ho impostato expiresIn opzione expiresIn sul tempo desiderato prima che l'utente sia costretto ad accedere nuovamente. Il mio è impostato su 30 minuti. Questo deve essere maggiore del valore di softexp .

Quando la mia app lato client invia una richiesta all'API del server (dove è richiesto il token, ad es. Pagina dell'elenco dei clienti), il server verifica se il token inviato è ancora valido o meno in base al valore di scadenza originale ( expiresIn ). Se non è valido, il server risponderà con uno stato particolare per questo errore, ad es. INVALID_TOKEN .

Se il token è ancora valido in base al valore expiredIn , ma ha già superato il valore softexp , il server risponderà con uno stato separato per questo errore, ad es. EXPIRED_TOKEN :

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

Sul lato client, se ha ricevuto la risposta EXPIRED_TOKEN , dovrebbe rinnovare automaticamente il token inviando una richiesta di rinnovo al server. Questo è trasparente per l'utente e viene automaticamente curato dall'app client.

Il metodo di rinnovo nel server deve verificare se il token è ancora valido:

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

Il server rifiuterà di rinnovare i token se non ha superato il metodo precedente.


Lavoro in Auth0 e sono stato coinvolto nella progettazione della funzione token di aggiornamento.

Tutto dipende dal tipo di applicazione ed ecco il nostro approccio raccomandato.

Applicazioni Web

Un buon modello è di aggiornare il token prima che scada.

Impostare la scadenza del token su una settimana e aggiornare il token ogni volta che l'utente apre l'applicazione Web e ogni ora. Se un utente non apre l'applicazione per più di una settimana, dovrà effettuare nuovamente l'accesso e questo è accettabile UX dell'applicazione Web.

Per aggiornare il token, l'API necessita di un nuovo endpoint che riceva un JWT valido, non scaduto e restituisca lo stesso JWT firmato con il nuovo campo di scadenza. Quindi l'applicazione Web memorizzerà il token da qualche parte.

Applicazioni mobili / native

La maggior parte delle applicazioni native effettua l'accesso una sola volta.

L'idea è che il token di aggiornamento non scade mai e può essere scambiato sempre con un JWT valido.

Il problema con un token che non scade mai è che non significa mai mai. Cosa fai se perdi il telefono? Pertanto, deve essere identificabile dall'utente in qualche modo e l'applicazione deve fornire un modo per revocare l'accesso. Abbiamo deciso di utilizzare il nome del dispositivo, ad esempio "iPad di maryo". Quindi l'utente può andare all'applicazione e revocare l'accesso a "iPad di maryo".

Un altro approccio è quello di revocare il token di aggiornamento su eventi specifici. Un evento interessante sta cambiando la password.

Riteniamo che JWT non sia utile per questi casi d'uso, quindi utilizziamo una stringa generata casualmente e la memorizziamo dalla nostra parte.


Nel caso in cui gestisci tu stesso l'author (ovvero non usi un provider come Auth0), potrebbe funzionare quanto segue:

  1. Emettere un token JWT con scadenza relativamente breve, ad esempio 15 minuti.
  2. L'applicazione controlla la data di scadenza del token prima di qualsiasi transazione che richieda un token (il token contiene la data di scadenza). Se il token è scaduto, chiede innanzitutto all'API di "aggiornare" il token (questo viene fatto in modo trasparente alla UX).
  3. L'API ottiene la richiesta di aggiornamento del token, ma prima controlla il database dell'utente per vedere se è stato impostato un flag 'reauth' rispetto a quel profilo utente (il token può contenere ID utente). Se il flag è presente, viene negato l'aggiornamento del token, altrimenti viene emesso un nuovo token.
  4. Ripetere.

Il flag 'reauth' nel backend del database verrebbe impostato quando, ad esempio, l'utente ha reimpostato la propria password. Il flag viene rimosso quando l'utente accede la volta successiva.

Inoltre, supponiamo che tu abbia una politica in base alla quale un utente deve accedere almeno una volta ogni 72 ore. In tal caso, la logica di aggiornamento del token API controlla anche l'ultima data di accesso dell'utente dal database dell'utente e nega / consente l'aggiornamento del token su tale base.


Una soluzione alternativa per invalidare i JWT, senza ulteriore spazio di archiviazione sicuro sul back-end, è implementare una nuova colonna intera jwt_version sulla tabella degli utenti. Se l'utente desidera disconnettersi o scadere i token esistenti, aumenta semplicemente il campo jwt_version .

Quando si genera un nuovo JWT, codificare jwt_version nel payload JWT, aumentando facoltativamente il valore in anticipo se il nuovo JWT deve sostituire tutti gli altri.

Quando si convalida JWT, il campo jwt_version viene confrontato con user_id e l'autorizzazione viene concessa solo se corrisponde.





jwt