node.js - 期限 - 更新 トークン




JWT(JSON Webトークン)の有効期限の自動延長 (6)

新しいREST APIにJWTベースの認証を実装したいと思います。 しかし、有効期限がトークンに設定されているので、それを自動的に延長することは可能ですか? ユーザーがその期間にアプリケーションを積極的に使用していた場合、X分ごとにサインインする必要はありません。 それは巨大なUXの失敗でしょう。

しかし、有効期限を長くすると、新しいトークンが作成されます(古いものは有効期限が切れるまで有効です)。 そして、各要求の後に新しいトークンを生成すると、私は馬鹿に聞こえます。 複数のトークンが同時に有効な場合、セキュリティ上の問題のように聞こえる。 もちろん、ブラックリストを使って古いものを無効にすることもできますが、トークンを保存する必要があります。 JWTのメリットの1つにストレージがありません。

私はAuth0がそれをどのように解決したかを見出した。 JWTトークンだけでなく、リフレッシュトークンも使用しhttps://docs.auth0.com/refresh-token : https://docs.auth0.com/refresh-token

しかし、これを(Auth0なしで)実装するには、リフレッシュトークンを保存し、有効期限を維持する必要があります。 それで本当のメリットは何ですか? なぜトークンを1つしか持たず(JWTではない)、サーバに期限を保つのはなぜですか?

他のオプションはありますか? このシナリオに適していないJWTを使用していますか?


jwt-autorefresh

ノード(React / Redux / Universal JS)を使用している場合、 npm i -S jwt-autorefreshインストールできます。

このライブラリは、アクセストークンが期限切れになるまでに計算された秒数(トークンにエンコードされたエクスペリメントクレームに基づいて)をユーザーに表示するようにJWTトークンのリフレッシュをスケジュールします。 広範なテストスイートを備え、いくつかの条件をチェックして、奇妙なアクティビティに環境からの誤設定に関する記述メッセージが付いてくることを確認します。

完全な実装例

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

免責条項:私は保守主義者です


Auth0のようなプロバイダを使用しないでauthを自分で処理する場合、次のように動作します:

  1. JWTトークンを比較的短期間で発行する。例えば15分。
  2. アプリケーションは、トークンを必要とするトランザクションの前にトークン有効期限をチェックします(トークンには有効期限があります)。 トークンの有効期限が切れている場合は、最初にAPIにトークンをリフレッシュするように要求します(これはUXに対して透過的に行われます)。
  3. APIはトークンのリフレッシュ要求を取得しますが、最初にユーザーデータベースをチェックして、そのユーザープロファイルに対して「reauth」フラグが設定されているかどうかを確認します(トークンにはユーザーIDが含まれます)。 フラグが存在する場合、トークンリフレッシュは拒否され、そうでない場合は新しいトークンが発行される。
  4. 繰り返す。

たとえば、データベースのバックエンドの 'reauth'フラグは、ユーザーがパスワードをリセットしたときに設定されます。 ユーザーが次回ログオンすると、フラグは削除されます。

さらに、ユーザーが72時間ごとに少なくとも1回はログインする必要があるポリシーがあるとします。 その場合、APIトークンリフレッシュロジックは、ユーザーデータベースからのユーザーの最終ログイン日をチェックし、その基準でトークン更新を拒否または許可します。


トークンデータに変数を追加することでこの問題を解決しました:

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

私はユーザーが再びログインする前に、 expiresInオプションを希望の時間に設定しexpiresInた。 鉱山は30分に設定されています。 これはsoftexpの値よりも大きくなければなりません。

私のクライアントサイドアプリケーションがサーバーAPI(トークンが必要な場所、例えば顧客リストページ)にリクエストを送信すると、サーバーは送信されたトークンが元の有効期限( expiresIn )値に基づいて有効かどうかをチェックします。 有効でない場合、サーバはこのエラーに特有のステータスで応答します。 INVALID_TOKEN

トークンがexpiredIn値に基づいてまだ有効であるが、すでにsoftexp値を超えてsoftexp場合、サーバーはこのエラーの別のステータスで応答します。 EXPIRED_TOKEN

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

クライアント側では、 EXPIRED_TOKEN応答を受信した場合、サーバーに更新要求を送信することによってトークンを自動的に更新する必要があります。 これはユーザーには透過的で、自動的にクライアントアプリケーションの世話をします。

サーバーの更新メソッドは、トークンが有効かどうかをチェックする必要があります。

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

上記の方法で失敗した場合、サーバーはトークンの更新を拒否します。


バックエンドでRESTful APIを使用してアプリケーションをHTML5に移行するときに、私は周りを見ていました。 私が思いついた解決策は次のとおりでした:

  1. クライアントは、ログインに成功すると、セッション時間30分(または通常のサーバー側のセッション時間)のトークンが発行されます。
  2. 有効期限が切れる前にトークンを更新するためにサービスを呼び出すためのクライアント側のタイマーが作成されます。 新しいトークンは、将来の呼び出しで既存のトークンを置き換えます。

ご覧のように、頻繁なリフレッシュトークン要求が減少します。 更新トークンコールがトリガーされる前にユーザーがブラウザー/アプリを閉じると、以前のトークンは期限切れになり、ユーザーは再ログインする必要があります。

ユーザーの非アクティブを満たすために、より複雑な戦略を実装することができます(たとえば、開かれたブラウザタブは無視されます)。 その場合、更新トークンコールは、定義されたセッション時間を超えてはならない予想される有効期限を含むべきです。 アプリケーションは、それに応じて最後のユーザーインタラクションを追跡する必要があります。

私は長い期限を設定するという考え方が嫌いです。したがって、このアプローチは、頻繁ではない認証を必要とするネイティブアプリケーションではうまく機能しない可能性があります。


私は実際にPHPでこれを実装しました.Guiクライアントを使用してapiのクライアントライブラリを作成しましたが、他のプラットフォームでも動作するはずです。

基本的には、短いトークン(5分)と長いトークン(1トークン)を1週間後に発行します。 クライアントライブラリは、ミドルウェアを使用して、何らかの要求に対して401応答を受け取った場合に、短いトークンの1回のリフレッシュを試みます。 それは元のリクエストを再度試み、リフレッシュできた場合には正しい応答をユーザに透過的に得る。 失敗した場合は、401をユーザーに送信します。

短いトークンが期限切れになっていても本物であり、長いトークンが有効かつ本物である場合、長いトークンが認証するサービス上の特別なエンドポイントを使用して短いトークンをリフレッシュします(これはこれを使用できる唯一のものです)。 次に、短いトークンを使用して新しい長いトークンを取得し、短いトークンをリフレッシュするたびに別の週を延長します。

また、この方法では最大5分以内にアクセスを取り消すことができます。これは、トークンのブラックリストを保存しなくても私たちの使用に適しています。

遅く編集:私の頭の中で新鮮だった後この月を読んで、より高価な呼び出しの機会を与えるので、あなたは短いトークンをリフレッシュするときにアクセスを取り消すことができることを指摘しておく必要があります(例えば、あなたのサービスへの1回の呼び出しごとに料金を支払わずに禁止されています)。


良い質問 - 質問自体に豊富な情報があります。

記事を更新する記事:それらを使用するタイミングとJWTとの対話方法は 、このシナリオでは良い考えです。 いくつかのポイントがあります: -

  • リフレッシュトークンは、新しいアクセストークンを取得するために必要な情報を保持します。
  • リフレッシュトークンも期限切れになる可能性がありますが、かなり長生きします。
  • リフレッシュトークンは、通常、漏洩しないように、厳重な保管要件に従います。
  • また、認可サーバーによってブラックリストに登録することもできます。

auth0/angular-jwt angularjsもauth0/angular-jwtください。

Web APIの場合。 ASP .NET Web API 2とOwinを使用してAngularJSアプリケーションでOAuthリフレッシュトークンを有効にする







jwt