node.js parser - JWT(Token Web JSON)prolungamento automatico della scadenza




security sign (9)

Ho risolto questo problema aggiungendo una variabile nei dati del token:

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

Ho impostato expiresIn opzione expiresIn al mio 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 l'app lato client invia una richiesta all'API del server (dove è richiesto token, ad esempio la pagina dell'elenco clienti), il server controlla 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 risposta EXPIRED_TOKEN , dovrebbe rinnovare automaticamente il token inviando una richiesta di rinnovo al server. Questo è trasparente per l'utente e viene automaticamente gestito 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.

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 utilizzando attivamente l'applicazione in quel periodo. Quello sarebbe un enorme fallimento UX.

Ma prolungare la scadenza crea un nuovo token (e il 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 allo stesso tempo. Ovviamente potrei invalidare il vecchio usato usando una lista nera ma avrei bisogno di memorizzare i token. E uno dei vantaggi di JWT non è l'archiviazione.

Ho trovato come Auth0 l'ha risolto. Utilizzano non solo 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 allora? Perché non hai un solo token (non JWT) e mantieni la scadenza sul server?

Ci sono altre opzioni? L'utilizzo di JWT non è adatto per questo scenario?


Buona domanda- e c'è una ricchezza di informazioni nella domanda stessa.

L'articolo Aggiorna ganci: quando utilizzarli e in che modo interagiscono con le JWT dà una buona idea per questo scenario. Alcuni punti sono: -

  • I token di aggiornamento portano 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 solitamente soggetti a severi requisiti di archiviazione per garantire che non siano trapelati.
  • Possono anche essere inseriti nella lista nera dal server di autorizzazione.

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

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


Stavo armeggiando quando spostavo le nostre applicazioni su HTML5 con apis RESTful nel backend. La soluzione che ho trovato è stata:

  1. Il client viene emesso con un token con un tempo di sessione di 30 minuti (o qualunque sia il tempo di sessione lato server usuale) dopo il login riuscito.
  2. Viene creato un timer lato client per chiamare un servizio per rinnovare il token prima del suo tempo di scadenza. Il nuovo token sostituirà l'esistente nelle chiamate future.

Come puoi vedere, questo riduce le richieste frequenti di token di aggiornamento. Se l'utente chiude il browser / l'app prima che la chiamata al rinnovo del token venga attivata, il token precedente scadrà nel tempo e l'utente dovrà effettuare nuovamente l'accesso.

Una strategia più complicata può essere implementata per soddisfare l'inattività dell'utente (ad esempio trascurata una scheda del browser aperta). In tal caso, la chiamata di rinnovo del token deve includere il tempo di scadenza previsto che non deve superare il tempo di sessione definito. L'applicazione dovrà tenere traccia dell'ultima interazione dell'utente di conseguenza.

Non mi piace l'idea di impostare la scadenza a lungo termine, quindi questo approccio potrebbe non funzionare bene con le applicazioni native che richiedono l'autenticazione meno frequente.


In realtà l'ho implementato in PHP usando il client Guzzle per creare una libreria client per l'API, ma il concetto dovrebbe funzionare per altre piattaforme.

Fondamentalmente, emetto due token, uno corto (5 minuti) uno e uno lungo che scade dopo una settimana. La libreria client utilizza il middleware per tentare un aggiornamento del token breve se riceve una risposta 401 ad alcune richieste. Riprenderà quindi la richiesta originale e, se è stata in grado di aggiornare, ottiene la risposta corretta, in modo trasparente all'utente. Se fallisce, invierà semplicemente il 401 all'utente.

Se il token breve è scaduto, ma ancora autentico e il token lungo è valido e autentico, aggiornerà il token breve utilizzando un endpoint speciale sul servizio che il token lungo autentica (questa è l'unica cosa per cui può essere utilizzato). Utilizzerà quindi il token breve per ottenere un nuovo token lungo, estendendolo in tal modo un'altra settimana ogni volta che aggiorna il token breve.

Questo approccio ci consente anche di revocare l'accesso entro al massimo 5 minuti, il che è accettabile per il nostro uso senza dover memorizzare una lista nera di token.

Modifica in ritardo: rileggere questo mese dopo che era fresco nella mia testa, vorrei sottolineare che è possibile revocare l'accesso quando si aggiorna il token breve perché dà l'opportunità di effettuare chiamate più costose (es. Chiamare il database per vedere se l'utente è stato vietato) senza pagare per ogni singola chiamata al tuo servizio.


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

Tutto dipende dal tipo di applicazione e qui è il nostro approccio raccomandato.

Applicazioni Web

Un buon modello è quello 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à riconnettersi e questa è un'applicazione UX accettabile.

Per aggiornare il token, l'API richiede un nuovo endpoint che riceve un JWT valido e non scaduto e restituisce 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 il login una sola volta e solo una volta.

L'idea è che il token di aggiornamento non scade mai e può essere scambiato sempre per 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 in qualche modo identificabile dall'utente 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ò accedere all'applicazione e revocare l'accesso a "maryo's iPad".

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

Crediamo che JWT non sia utile per questi casi d'uso, quindi usiamo una stringa generata a caso e la archiviamo dalla nostra parte.


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 a un numero calcolato dall'utente di secondi prima che il token di accesso scada (in base alla dichiarazione exp codificata nel token). Ha una vasta suite di test e controlla per un certo numero di condizioni per garantire che ogni strana attività sia accompagnata da un messaggio descrittivo riguardante le errate configurazioni del proprio 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: io sono il manutentore


Che ne dici di questo approccio:

  • Per ogni richiesta del client, il server confronta la scadenza del token con (currentTime - lastAccessTime)
  • Se expirationTime <(currentTime - lastAccessedTime) , cambia l'ultimo lastAccessedTime in currentTime.
  • In caso di inattività sul browser per un periodo di tempo superiore alla scadenza o nel caso in cui la finestra del browser fosse chiusa e il tempo di scadenza> (currentTime - lastAccessedTime) , il server può scadere il token e chiedere all'utente di effettuare nuovamente il login.

In questo caso non è necessario un endpoint aggiuntivo per l'aggiornamento del token. Apprezzerei qualsiasi pacco.


Nel caso in cui si gestisca l'auth (cioè non si usi un provider come Auth0), quanto segue potrebbe funzionare:

  1. Emetti 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 richiede un token (il token contiene la data di scadenza). Se il token è scaduto, prima chiede all'API di "aggiornare" il token (questo viene fatto in modo trasparente per l'UX).
  3. L'API riceve la richiesta di aggiornamento del token, ma prima controlla il database utente per vedere se un flag 'reauth' è stato impostato su quel profilo utente (il token può contenere l'id utente). Se il flag è presente, viene negato l'aggiornamento del token, altrimenti viene emesso un nuovo token.
  4. Ripetere.

Il flag "reauth" nel back-end del database verrà impostato quando, ad esempio, l'utente ha reimpostato la propria password. Il flag viene rimosso quando l'utente effettua l'accesso la volta successiva.

Inoltre, diciamo che hai 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 controllerà anche l'ultima data di accesso dell'utente dal database utente e negherà / consentirà l'aggiornamento del token su tale base.


La differenza principale è la dimensione della memoria di sessione e il lavoro di ricerca richiesti dal server:

  • Sul lato server, JWT memorizza una singola chiave in memoria (o nel file di configurazione) - chiamata chiave segreta . Quella chiave ha due scopi, consente di creare nuovi token crittografati e funziona anche come una chiave master che "apre tutte le serrature" - o nella vita reale verifica tutti i token. Di conseguenza il server risponde molto più velocemente alle richieste di autenticazione, perché non importa se hai due o due milioni di utenti loggati - lo stesso numero di record (uno, quella chiave del server) verrà utilizzato per autenticare tutte le richieste dei client.

  • Autenticazione tradizionale che memorizza le sessioni utente in un database, crea un record nel db per ogni singolo utente, che risulta in più chiavi . Quindi se hai due milioni di utenti loggati, il server creerà due milioni di record e con ogni richiesta del client il server dovrà individuare il record della sessione pertinente nel database *.

JWT lo lascia sul lato client per archiviare e gestire l'intera sessione / oggetto utente. In realtà ha molto più senso perché ogni client gestisce solo i propri dati, quindi non provoca un sollevamento pesante per il lato client.

Per quanto riguarda ciò che hai scritto nel tuo ultimo paragrafo, non sono solo le chiamate db che salviamo qui. JWT è in realtà molto più scalabile a causa della sua natura indipendente e leggera, non fallisce quando le richieste di autenticazione si accumulano e consente al server di gestire l'autenticazione attraverso dispositivi e servizi senza gestire le sessioni sul lato server.

Per quanto riguarda la sicurezza, le sessioni in db hanno probabilmente il sopravvento: possono essere più sicure a causa di tale latenza e sono anche meno vulnerabili al dirottamento di sessione dopo il logout dell'utente.

* Il metodo delle sessioni memorizzate db può essere ottimizzato con un caching efficace e memorizzando solo l'id di sessione (anziché l'intero oggetto utente) in un server chiave / valore veloce come Redis. Detto questo, sceglierei comunque il metodo JWT su db per la maggior parte dei casi.





node.js api security authentication jwt