Feathers: 更新トークンのサポートを追加する

作成日 2015年12月22日  ·  64コメント  ·  ソース: feathersjs/feathers

現在、有効な認証トークンを<loginEndpoint>/refresh投稿することで、新しいトークンを取得できます。 ここで説明するように、更新トークンのワークフローは少し異なります。
https://auth0.com/learn/refresh-tokens

Authentication Feature

最も参考になるコメント

React Nativeアプリに関しては、この機能は必須です。 ユーザーは最初にログインし、数週間後にアプリを開いたときに、まだログインしていることを期待しています。

全てのコメント64件

:+1: @corymsmithと私はこれについて話していました。 「休日」のフィニッシュラインでこれのいくつかを蹴るのを手伝うことを望んでいます。

マスターでこれをサポートしていますが、 decouplingブランチでもサポートしています。 トークンを更新するには、次の2つのオプションがあります。

  1. 電子メール/パスワード、Twitterなどを使用して再認証できます。
  2. 有効なトークンをGET /auth/token/refresh渡すことができます

トークンの更新プロセスは実施されていますが、上記のAuth0リンクで説明されているように、完全な更新トークンのサポートは完全ではありません。 実際の更新トークンはGitHub認証コード/パスワードと同様に機能しますが、新しいJWTトークンを取得するためにのみ使用できます。 したがって、JWTトークンの有効期限が切れた場合でも、更新トークンがある場合は、それを使用して再度ログインできます。 それらはuserIdをそのままにしてデータベースに永続化され、いつでも取り消すことができます。 少なくとも、それは私がAuth0の記事から集めているものです。

ああ、あなたは正しい@marshallswainです。 リンクをクリックする必要があったと思います:wink:

最初のカットでは、これを1.0マイルストーンから外すと思います。 人々が再認証するのは簡単です。

これをfeathers-authentication 2.0のものにすることに投票します。

素晴らしい心。

認証ワークフローがどのように正確に機能するかが明確でないため、私はまだかなり混乱しています。

私が今していることはです。
1.)クライアントがユーザー名とパスワードを送信する

 curl -X POST https://xxx/auth/local   -H "Content-Type: application/json"   -d '{ "email":"xxx", "password":"yyy"}'

これはJWTトークンを返します。

{"token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJfaWQiOiI1NzhhNjUyN2RkMTZiMjIwMDRhY2ZjNmEiLCJpYXQiOjE0NzAzMjYyODUsImV4cCI6MTQ3MDQxMjY4NSwiaXNzIjoiZmVhdGhlcnMifQ.OVvQbnxfoDGxPFm3Y6tBhRae2Qa6_mDq-PVIo8RcC8Y"}

2.)次に、このトークンをAuthorizatin httpヘッダーに入れて、APIにアクセスします。

curl -X GET https://xxx/users  -H 'Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJfaWQiOiI1NzhhNjUyN2RkMTZiMjIwMDRhY2ZjNmEiLCJpYXQiOjE0NzAzMjU1NzYsImV4cCI6MTQ3MDQxMTk3NiwiaXNzIjoiZmVhdGhlcnMifQ._CHdx3RpEuI189t90mXq-IMPXRNuoVh7nBwY1ON7xCY'

私が理解していないのは、次にこのトークンを実際に更新する方法です。
私が試したのは、このトークンをxxx / auth / token / refreshに送信することです
私が得たのは、もう1つの非常に長いトークンです。 次に、古いトークンとこの新しいトークンの両方を使用してAPIにアクセスしようとしました。 両方とも機能します...(古いものを無効にすべきではありませんか?)

curl -X GET https://xxx/auth/token/refresh  -H 'Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJfaWQiOiI1NzhhNjUyN2RkMTZiMjIwMDRhY2ZjNmEiLCJpYXQiOjE0NzAzMjU1NzYsImV4cCI6MTQ3MDQxMTk3NiwiaXNzIjoiZmVhdGhlcnMifQ._CHdx3RpEuI189t90mXq-IMPXRNuoVh7nBwY1ON7xCY'
{"query":{"token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJfaWQiOiI1NzhhNjUyN2RkMTZiMjIwMDRhY2ZjNmEiLCJpYXQiOjE0NzAzMjU1NzYsImV4cCI6MTQ3MDQxMTk3NiwiaXNzIjoiZmVhdGhlcnMifQ._CHdx3RpEuI189t90mXq-IMPXRNuoVh7nBwY1ON7xCY"},"provider":"rest","token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJxdWVyeSI6eyJ0b2tlbiI6ImV5SjBlWEFpT2lKS1YxUWlMQ0poYkdjaU9pSklVekkxTmlKOS5leUpmYVdRaU9pSTFOemhoTmpVeU4yUmtNVFppTWpJd01EUmhZMlpqTm1FaUxDSnBZWFFpT2pFME56QXpNalUxTnpZc0ltVjRjQ0k2TVRRM01EUXhNVGszTml3aWFYTnpJam9pWm1WaGRHaGxjbk1pZlEuX0NIZHgzUnBFdUkxODl0OTBtWHEtSU1QWFJOdW9WaDduQndZMU9ON3hDWSJ9LCJwcm92aWRlciI6InJlc3QiLCJ0b2tlbiI6ImV5SjBlWEFpT2lKS1YxUWlMQ0poYkdjaU9pSklVekkxTmlKOS5leUpmYVdRaU9pSTFOemhoTmpVeU4yUmtNVFppTWpJd01EUmhZMlpqTm1FaUxDSnBZWFFpT2pFME56QXpNalUxTnpZc0ltVjRjQ0k2TVRRM01EUXhNVGszTml3aWFYTnpJam9pWm1WaGRHaGxjbk1pZlEuX0NIZHgzUnBFdUkxODl0OTBtWHEtSU1QWFJOdW9WaDduQndZMU9ON3hDWSIsImRhdGEiOnsiX2lkIjoiNTc4YTY1MjdkZDE2YjIyMDA0YWNmYzZhIiwiaWF0IjoxNDcwMzI1NTc2LCJleHAiOjE0NzA0MTE5NzYsImlzcyI6ImZlYXRoZXJzIiwidG9rZW4iOiJleUowZVhBaU9pSktWMVFpTENKaGJHY2lPaUpJVXpJMU5pSjkuZXlKZmFXUWlPaUkxTnpoaE5qVXlOMlJrTVRaaU1qSXdNRFJoWTJaak5tRWlMQ0pwWVhRaU9qRTBOekF6TWpVMU56WXNJbVY0Y0NJNk1UUTNNRFF4TVRrM05pd2lhWE56SWpvaVptVmhkR2hsY25NaWZRLl9DSGR4M1JwRXVJMTg5dDkwbVhxLUlNUFhSTnVvVmg3bkJ3WTFPTjd4Q1kifSwiaWF0IjoxNDcwMzI2NDQyLCJleHAiOjE0NzA0MTI4NDIsImlzcyI6ImZlYXRoZXJzIn0.TqUv3051TTGbX4cPfkN-6pOOB5SN9nH-E7TU1HHSsb8","data":{"_id":"578a6527dd16b22004acfc6a","iat":1470325576,"exp":1470411976,"iss":"feathers","token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJfaWQiOiI1NzhhNjUyN2RkMTZiMjIwMDRhY2ZjNmEiLCJpYXQiOjE0NzAzMjU1NzYsImV4cCI6MTQ3MDQxMTk3NiwiaXNzIjoiZmVhdGhlcnMifQ._CHdx3RpEuI189t90mXq-IMPXRNuoVh7nBwY1ON7xCY"}}

さらに奇妙なことに、この新しいトークンを使用して、/ auth / token / refreshに再度送信しようとしました。
私はこれよりもさらに長いトークンを手に入れました。

ここで何を間違えたのか、誤解したのかわかりません。 提案してください。

@parnurzealまだ更新トークンのサポートはありません。 これが提案された機能である理由です。

新しいトークンを取得する方法は、既存の有効なJWTを使用して/auth/token POSTを実行するか、別の認証メカニズムを使用してログインすることです。 あなたはすべてを正しくやっているようです。

正しいですが、私がどのようにしたか、そして私が得た結果を注意深く見てください。

簡単な例を使ってみましょう。
abcdefghijklmnoを使用して新しいトークンを要求したとき(ランダムなナンセンストークンのみ)。
応答は前のトークンの単なる長いバージョンです-> abcdefghijklmnopqrstuvwxyz
abcdefghijklmnopqrstuvwxyzを使用してもう一度実行しようとすると、より長いバージョンが取得されます->
abcdefghijklmnopqrstuvwxyz1234567890とループが続きます(より多くを要求すると、前のもののより長いバージョンを取得します)。

また、上記の3つのトークンはすべて同時に使用できます。
新しいトークンをリクエストした後、前のトークンの有効期限が切れるべきではありませんか?

@parnurzeal私が言っているのは、その機能が実際には実装されていないため、あなたがしたことをしないということです。 実装に基づくと(これまでのところ)、 /auth/token/refresh到達するたびにトークンが増え続けるという事実は、データをトークンに押し戻すだけだからです。 これはそれが機能することを意図した方法ではなく、私たちはそれを完了する時間がなく、なぜこれが文書化されていないのですか? あなたはそれを使うことになっていない。

新しいトークンをリクエストした後、前のトークンの有効期限が切れるべきではありませんか?

これがJWTの性質です。 それらはTTLから自動的に期限切れになります。 まだ有効期限が切れていない古いトークンが使用されないようにする場合は、ブラックリストを維持する必要があります。 現在、これはあなたに任されており、それに関する未解決の問題(#133)がありますが、(もしあれば)すぐには解決しない可能性があります。

こんにちは、/ auth / refresh / tokenを調べたところ、次のようなものが見つかりました。

...
function pick (o, ...props) {
  return Object.assign({}, ...props.map(prop => ({[prop]: o[prop]})));
}

// Provider specific config
const defaults = {
  payload: ['id', 'role'],
  passwordField: 'password',
  issuer: 'feathers',
  algorithm: 'HS256',
  expiresIn: '1d', // 1 day
};
...
// GET /auth/token/refresh
  get (id, params) {
    if (id !== 'refresh') {
      return Promise.reject(new errors.NotFound());
    }

    const options = this.options;

    // Add payload fields
    const data = pick(params.payload, options.payload);

    return new Promise(resolve => {
      jwt.sign(data, config.get('auth').token.secret, options, token => {
        return resolve({token: token});
      });
    });

  }

実装としてはナイーブすぎますか? そうでない場合は、磨きをかけることができます。いくつかのテストを追加して、PRを作成してください。

@aboutlo努力してくれてありがとう! たくさんの変更が行われ、そのルートが今週なくなる可能性があるため、v0.8がリリースされるまで待つのが最善です(しばらくの間アルファ版になっています)。
今日はベータリリースをカットし、現在移行ガイドをまとめています。 したがって、長くはかからず、v0.8はauthに関する現在の問題の多くに対処します。

トークンを更新することについて多くのことを考えてきたので、0.8がリリースされたら(今週)、この問題について話し合いたいと思います。 今週後半に予備的な考えを述べるでしょう。

十分に公平な@ekryski 、私は0.8を待ちます:)

React Nativeアプリに関しては、この機能は必須です。 ユーザーは最初にログインし、数週間後にアプリを開いたときに、まだログインしていることを期待しています。

@deiucanta良いニュースは、 auth @ 1.0を設計するです。 それを適切に配置して文書化するまで、そう長くはかからないと思います。

それは良いニュースです! 👍それを楽しみにしています

@marshallswainこの機能の更新を楽しみにしています。 いつ期待できるか教えてください。 それともすでにリリースされていますか? 前もって感謝します。

@deiucanta当面の間、この機能がリリースされるまでは、より長寿命のトークンを使用できます。 リリースされたら、認証シークレットを新しい値にローテーションして、既存のすべてのセッションを消去し、すべてのユーザーをより短い更新セッションに参加させることができます。

@atulrpandeyは正式にはリリースされていませんが、実装するのも難しくありません。 フックを追加して新しい更新トークンを生成し、それをDBのユーザーオブジェクトに保存します。使用されたり期限切れになったりしたら、ユーザーから削除します。

@petermikitsh (モバイルを使用している場合)もう1つの方法は、 clientIdclientSecretクライアントに安全に保存し、JWT accessTokenの有効期限が切れた場合は、それらを使用して再認証することです。

@ekryskiこれらの戦略のいずれかの例を教えてください。 それは本当に役に立ちます。
または、公式サポートがリリースされるまでに時間がかかりますか? これは本当にモバイル認証に役立ちます!

更新トークンのトピックについて:

更新トークンには、新しいアクセストークンを取得するために必要な情報が含まれています。 つまり、特定のリソースにアクセスするためにアクセストークンが必要な場合は常に、クライアントは更新トークンを使用して、認証サーバーによって発行された新しいアクセストークンを取得できます。 一般的な使用例には、古いアクセストークンの有効期限が切れた後に新しいアクセストークンを取得することや、新しいリソースに初めてアクセスすることが含まれます。 更新トークンも期限切れになる可能性がありますが、寿命はかなり長くなります。 更新トークンは通常、リークされないようにするために厳格なストレージ要件の対象となります。 それらは、許可サーバーによってこともできます。 -https://auth0.com/blog/refresh-tokens-what-are-they-and-when-to-use-them/

だから私は思ったよりも早くReactNativeに入った。 私はおそらくこれに貢献することを考えていますが、それが正しい実装であることを確認するために、メカニズムを完全に理解していることを確認したいと思います。 更新トークンはステートレスではないため、使用法にいくつかの制約があります(たとえば、開発者はストレージアダプターを提供する必要があります)。

有効なJWTを使用して更新トークンを取得できますか? または、更新トークンが認証応答で自動的に返されますか(たとえば、 accessToken )? https://auth0.com/learn/refresh-tokens/の例では、Auth0が認証応答( accessTokenrefreshToken両方)にそれらを含めているようです

@petermikitsh有効なJWTを使用して更新トークンを取得できるとは思わない。 そうすれば、誰でもJWTを取得して、アカウントへのアクセスを維持できます。

更新トークンは通常、ログイン/サインアップ応答とともに返されます。その後、クライアントは、新しいセッションと新しい更新トークンを提供する再度ログイン/サインアップしない限り、特定のセッションでそれらに再度アクセスすることはできません。

更新トークンは実際には有効期限が切れている必要はありませんが、データベースに保存されている場合は呼び出すことができるため、ユーザーはアクティブなセッションの数を確認することもできます。 更新トークンを発行するときに、より多くの情報を保存できます(os、ip、デバイス名など、FacebookやGitHubのように識別可能にします)。

少なくともそれが私のやり方です。

更新トークンを発行するときと更新トークンを使用しようとするときの両方で認証を確認する限り、有効なJWTを使用して更新トークンを取得しても問題ありません。

@marshallswain

現在、有効な認証トークンを<loginEndpoint>/refresh投稿することで、新しいトークンを取得できます。 更新トークンのワークフローは少し異なります...

したがって、混乱を避けるために、 refreshではなくrenewと呼ぶ必要があります<loginEndpoint>/renew

@abhishekbhardwajが言ったように

accessTokenは、accessTokenによって更新可能であってはならず、refresehTokenまたはユーザー名/パスワードによってのみ更新可能である必要があります。refreshTokenは、ユーザー/パスワード認証、または2要素のようにブラウザーからアクセスできないその他のシークレットによってのみ更新可能である必要があります。認証...

現在、ここに記載されているように、accessTokenでaccessTokenを更新することが可能です。

https://github.com/feathersjs/authentication-jwt/issues/61

別のアプローチは、accessTokenのペイロード内に更新トークンを格納し、現在の更新APIが更新トークンが(データベースまたはredis呼び出しを介して)取り消されていないかどうかを確認することです。 このように、更新トークンは単純な自動インクリメントIDである可能性があります。 また、リフレッシュAPIは有効期限をチェックしなくなりました。 リフレッシュトークン(単純整数)はアクセストークンで署名されているため、安全です。

この方法では、現在のコードベースへの変更を最小限に抑える必要があります。refreshapiの場合、ユーザーがアクセストークンが取り消されていないかどうかを確認するためのフックを提供できるようにする方法を提供します(ペイロード内の更新トークンを確認することにより)。 t有効期限をもう検証します。

このアプローチについてもう少し@ arash16について説明して

更新トークンをaccessTokensペイロードに保存し、更新APIが有効期限をチェックしない場合は、すべてのaccessTokenを効果的に「有効期限なし」にしただけではありませんか。

新しいアクセストークンを取得するために任意のaccessTokenを使用できるので、そうですか?

私は何かが足りないのですか?

@BigAB
リフレッシュAPIについてのみ有効期限をチェックしないことを意味しました。同じaccessTokenがリフレッシュトークンとアクセストークンの両方として使用されます。 このトークンは、更新して新しいアクセストークンを取得する場合にのみ有効期限が切れません。更新ID自体は、ユーザーが手動で取り消すことができます。

開発者は、すべての更新IDを格納するためのdb-table / redisを持っている必要があります。 ユーザーが他の一部(またはすべて)のセッションを取り消すかサインアウトする必要がある場合、すべての更新IDのリスト(およびブラウザーや作成日などの他の追加情報)を提供し、削除(サインアウト)することを選択します。 -から)それらを選択的に。 その後、これらのrefresh-idを含む実際のトークンの有効期限が切れると、refreshapiは新しいトークンの提供を拒否します。

トークン内のrefresh-idはほとんどの場合使用されず、トークンの有効期限が切れるまで認証はステートレスになります。その後、dbを1回呼び出して、refresh-idを検証し、新しいアクセストークンを返すことができます。

アクセストークンの有効期限は短い(10分未満)可能性があり、ユーザーはページを閉じて立ち去ることができます。後でページを開くと、アクセストークンはすでに有効期限が切れており、ログアウトしています。 ただし、トークン内のrefresh-idは、データベースによって管理される存続期間がはるかに

セキュリティの観点から、この方法で使用されるアクセストークンは、古いセッションキーのように扱う必要があります。さらに、データベースを呼び出して毎回検証する必要がないという利点もあります(有効期限が切れた場合のみ)。

@ arash16 、アクセスJWT内に更新トークンを保存するというあなたのアイデアが好きです。 サーバー側でこの更新トークンを取得する方法の例はありますか?

私の現在の問題:アクセストークンの有効期限が切れている場合、ペイロードはフェザーのフックコンテキストで使用できません。 app.service('authentication').hooks({ before: { create: ... } })最初など、 @feathersjs/authenticationパッケージのverifyJWT()ユーティリティ関数を使用する方法があると思いますか?

現在、フェザーで更新トークンを使用するための注意深い方法はありますか? それらのサポートを追加する計画はありますか?

皆さんこんにちは 、

それはまだ利用できないようです(または私は何かが足りないのですか)。 いつ利用可能になるか教えていただけますか? パスポート更新トークンリポジトリが利用可能であることがわかります。 誰もがこれを試しましたか?
https://github.com/fiznool/passport-oauth2-refresh

こんにちは@daffl
誰かが私がグーグル戦略のためにトークンを更新する方法を理解するのを手伝ってもらえますか? グーグルログインシナリオのパスワードがないので?

ありがとう

別のアプローチは、accessTokenのペイロード内に更新トークンを保存することです

したがって、期限切れのアクセストークンを1つでも持っ

更新トークンはクライアントに安全に保存する必要があり、このクライアント以外の誰もそれらにアクセスできないようにする必要があります。

@deiucanta良いニュースは、 auth @ 1.0を設計するです。 それを適切に配置して文書化するまで、そう長くはかからないと思います。

これは2016年に投稿されました。皆さん、この必須機能をサポートする計画はまだありますか?

いいえ。期限切れのトークンでは何もできません。 また、 v4プレリリースでは、更新トークンがはるかに簡単に可能に

また、 v4プレリリースでは、更新トークンがはるかに簡単に可能に

おおよそのリリース日はありますか?

それを行う方法のクックブックエントリは、最終リリースの一部になります。

このガイドがリリースされるまで、v4プレリリースでどのように実行できるかを簡単に説明していただけますか?

@daffl ping

これを行う公式の方法はまだありますか? v4がここにあり、ドキュメントに何も表示されません

@dafflは、これが4.0で、認証サービスへのハッキングなしでどのように達成できるかを詳しく説明できますか?

回避策としての@MichaelErmerは、ローカルまたは任意のカスタム戦略を使用してjwtを更新できますが、理想的ではありませんが、ワーカーとapiの間などの内部通信には問題なく機能します。

function initAuth() {
  return async (ctx) => {
    if (ctx.path !== 'authentication') {
      const [authenticated, accessToken] = await Promise.all([
        ctx.app.get('authentication'),
        ctx.app.authentication.getAccessToken(),
      ]);

      if (!accessToken || !authenticated) {
        const result = await ctx.app.authenticate(apiLocalCreds);
        ctx.params = {
          ...ctx.params,
          ...result,
          headers: { ...(ctx.params.headers || {}), Authorization: result.accessToken },
        };
      } else {
        const { exp } = decode(accessToken);
        const expired = Date.now() / 1000 > exp - 60 * 60;
        if (expired) {
          const result = await ctx.app.authenticate(apiLocalCreds);
          ctx.params = {
            ...ctx.params,
            ...result,
            headers: { ...(ctx.params.headers || {}), Authorization: result.accessToken },
          };
        }
      }
    }
    return ctx;
  };
}

client
  .configure(rest(apiHost).superagent(superagent))
  .configure(auth(authConfig))
  .hooks({ before: [initAuth()] });

現在、v4 authenticationでこのafterフックを使用して、20日後にaccessTokenを更新しています...

`` `` javascript
const {DateTime} = require( 'luxon')
const renewAfter = {日数:20}

module.exports =()=> {
非同期コンテキストを返す=> {
もしも (
context.method === 'create' &&
context.type === '後' &&
context.path === '認証' &&
context.data && context.data.strategy === 'jwt' &&
context.result &&
context.result.accessToken){
//トークンを更新する必要があるかどうかを確認します
constペイロード= await context.app.service( 'authentication')。verifyAccessToken(context.result.accessToken)
const issuesAt = DateTime.fromMillis(payload.iat * 1000)
const renewAfter = issueAt.plus(renewAfter)
const now = DateTime.local()
if(now> renewAfter){
context.result.accessToken = await context.app.service( 'authentication')。createAccessToken({sub:payload.sub})
}
}
コンテキストを返す
}
}
「」

このフックをafterに入れ、最後のフックとして使用して、すべての検証などに合格することが重要です。

リフレッシュトークンをフェザーに統合する計画はありますか?

私はその質問の1つ前のメッセージを2番目にしています。

更新トークンのワークフローについても疑問に思っています。 @ m0dch3nによってドラフトされたソリューションは良い習慣ですか? 別の方法で実装する必要がありますか?

私の意見のrefreshTokenワークフロー全体は、中間者攻撃からほんの少ししか保護しないため、中間者がaccessTokenを盗んだ場合、少なくともそれを更新することはできず、リソースに無限にアクセスできます。

XSSから保護することはできません。その場合、攻撃者はクライアント側に保存されているものをすべて盗むことができるからです。 したがって、refreshToken ...

ここでの問題は、accessTokenの有効期限を短くしすぎると(つまり5分)、更新の頻度が高くなりすぎることです。 真ん中の男性は、refreshTokenをインターセプトするために、クライアントの要求を5分間だけリッスンする必要があります...有効期限を長くすると、accessTokenだけでアクセスが長くなります...

正直なところ、一部のクライアントからアクセスが盗まれたと言われた場合は、とにかくaccessTokenrefreshTokenをブラックリストに登録する必要があります。 とにかく、私はリクエストごとにDBリクエストをすることを余儀なくされています。

私の場合、そのようなケースに気付いたとき、accessTokenの有効期間は40日であるため、過去40日間のすべてのaccessTokenをブラックリストに登録します...

HTTPSリクエストを使用すると、中間者攻撃が非常に困難になります。 HTTPSリクエストを使用していませんか?

もちろん私はhttpsを使用していますが、accessTokenを盗むには3つの可能性があります。 1つ目はクライアント側(XSS ie)、2つ目はトランスポート(中間者)、3つ目はサーバー側です。

クライアントとトランスポートでは、セキュリティの責任は私が半分だけで、残りの半分はクライアントですが、完全に私の管理下にあるわけではありません。 しかし、XSSを不可能にし、httpsでトランスポートを保護することで、セキュリティリスクを回避するために、クライアントを支援することができます...

refreshTokenの目標は、accessTokenの有効期限を短くし、各リクエストでより長いまたは無限の有効なトークンを送信しないようにすることです。

したがって、それがもたらす唯一のセキュリティは、100のリクエストから、つまり、100のすべてをトランスポートで脆弱にするのではなく、1つのリクエストだけにすることです。

つまり、基本的に中間者攻撃では、refeshTokenで保護することはできず、もちろんXSSで保護することもできません...このrefreshTokenを送信する回数によってのみ、削減できます...送信コストを削減できますただし、accessTokenはより長く有効である必要があります...

Slackチャンネルからコメントをコピーして貼り付けます。

更新トークンは必須のサポート機能であり、既存のアクセストークンを自動的に更新することではないと思います。 アクセストークンはステートレスであり、サーバー側に保存されません。 欠点は、それが永久に有効であるということです! アクセストークンが長いほど、より多くのリスクが課せられます。 ただし、アクセストークンが短すぎると、ユーザーは頻繁にログインする必要があり、使いやすさに大きな影響を与えます。

そこで、リフレッシュトークンが登場します。これは、アクセストークンをリフレッシュするために使用されるトークンであり、ロングライブトークンです。 アクセストークンの有効期限が切れると、クライアントは更新トークンを使用して新しいアクセストークンを取得できます。これが、更新トークンの唯一の目的です。

ユーザーアカウントが侵害された場合、更新トークンは取り消すことができます。 これが、アクセストークンと更新トークンの大きな違いです。 発行された更新トークンを取り消すには、サーバーは発行されたすべての更新トークンを保存する必要があります。 つまり、更新トークンはステートフルです。 サーバーは、どちらが有効で、どちらが無効であるかを知る必要があります。

更新トークンを適切に実装するには、更新トークンを永続化するための何らかのトークンストアが必要です。 また、少なくとも3つのフローを実装する必要があります。

トークン検証の更新
有効な更新トークンでアクセストークンを更新します
侵害されたユーザーの更新トークンを取り消す

トークン使用統計など、他にも便利な管理機能があります。

上記は、更新トークンを実装する方法に関する私の現在の理解です。 簡単なことではありませんが、より安全なシステムを構築する必要があります。

リフレッシュトークンを適切に実装するために必要なすべての機能/モジュールがすでに組み込まれていることが判明しました。

  1. リフレッシュトークンストア:FeathersServiceで簡単にサポートできます。
  2. 更新トークンの発行と検証:AuthenticationServiceが組み込まれている既存のJWTサポートを再利用できます。

TheSinding(https://github.com/TheSinding/authentication-refresh-token)によって行われた作業に基づいて、1つのカスタムサービスと3つのフック(https://github.com/)を使用して独自のバージョンのrefresh-tokenを実装しました。 jackywxd / feathers-refresh-token)これにより、基本的なリフレッシュトークン機能が有効になります。

  1. ユーザー認証が正常に行われた後、更新トークンを発行します。
  2. 有効なJWTリフレッシュトークンを使用してアクセストークンをリフレッシュします。
  3. 更新トークンを削除してユーザーをログアウトします

Feathresの既存のコードベースを完全に活用しながら、実際のコーディング作業は最小限であり、現在のFeathersアーキテクチャとうまく統合されます。 これは、現在のFeathersアーキテクチャが非常に拡張可能であることを証明しています。

ただし、更新トークンの全機能には、クライアント側での更新トークンの保存、アクセストークンの有効期限後のユーザーの再認証、更新トークンを使用したログアウトユーザーなど、クライアント側でのサポートも必要です。

feathers-authenticationとauthentication-clientのソースコードを確認した後、refresh-tokenを既存のFeaturesコードに利用して、認証をオンにするのと同じくらい簡単にrefresh-tokenサポートをオンにできると思います。

フックバージョンの更新トークンコードベースを@feathersjs / authenticationにすでに移植しました。 次に、authentication-clientを変更して、クライアント側の機能を有効にしようとします。 私の最終的な目標は、サーバー側とクライアント側の両方でリフレッシュトークンのサポートを有効にすることです。

私の質問/懸念は、更新トークンがクライアントにどのように保存されるかということです。

https://auth0.com/blog/securing-single-page-applications-with-refresh-token-rotation/を参照して

残念ながら、ブラウザには目的のアプリケーションのみによるアクセスを保証できる永続的なストレージメカニズムがないため、長寿命のRTはSPAには適していません。 これらの価値の高いアーティファクトを取得し、悪意のあるアクターに保護されたリソースへのアクセスを許可するために悪用される可能性のある脆弱性があるため、SPAで更新トークンを使用することは強くお勧めしません。

https://afteracademy.com/blog/implement-json-web-token-jwt-authentication-using-access-token-and-refresh-tokenを参照して

では、トークンを安全に保管するための最良の場所はどこですか? 完全に安全なストレージを実現することに情熱を注いでいる場合は、インターネットで詳細を読むことができます。 一部のソリューションは理想的ですが、あまり実用的ではありません。 実際には、httpOnlyフラグとSecureフラグを付けてCookieに保存します。 100%安全というわけではありませんが、仕事は終わります。

クッキーに関するこの長い議論を参照してください-https://github.com/feathersjs-ecosystem/authentication/issues/132

@bwgjoseph正規表現セッションを使用し、クライアント側ではなく、すべてのトークンをそこに保存することをお勧めします。 それが私がしていることであり、SPAを含むすべてのタイプのアプリで完全に正常に機能します

@sarkistltすべてのクライアントJWTトークンをサーバー側に保存すると言う

@bwgjosephいつもと同じように、cookie、サービスを登録する前に

app.use('* | [or specific rout]', session(sess), (req, res, next) => {
      req.feathers.session = req.session || {};
      next();
    });

次に、サーバーで、たとえば顧客ログインサービスの場合、顧客が認証されると、 ctx.params.session.token = tokenようにセッションにトークンを保存します。トークンはJWTアクセスまたは更新トークンであり、アプリケーションロジックによって異なります。
また、クライアントからの新しいリクエストでは、トークンがセッションに存在するかどうかを確認し、認証に使用します。 これは、クライアント側でトークンがまったく公開されていないため、はるかに安全で安全なアプローチです。

これがクライアント(ブラウザ)、つまりサーバーアプリケーションに最適であることを付け加えておきます。 サーバー間、またはワーカー/サーバー間で内部的に通信する場合、セッションは必要ありません。

これは以前に「たくさん」議論されており( FAQにも

したがって、通常はトークンをたとえばlocalStorage(現在のページのみがアクセスできる)に保存しても問題ありません。また、他の非ブラウザープラットフォーム(ネイティブモバイルアプリ、サーバー間など)ともシームレスに機能します。 websockets __(WebSocketをHTTP Cookieでシームレスかつ安全に機能させるのがどれほど苦痛であるかを強調することはできません。そうしようとしないことで、私の生活はずっと楽になりました)。 一般に、更新トークンは、通常ははるかに長寿命であるため、取り消すことができます。

いずれにせよ、これに対するプルリクエストは大歓迎です。詳細を簡単に解決できると思います。

@ jackywxd-すばらしい仕事です。簡単に見てみましたが、いくつかのすばらしい追加のようです。
開発者がこれを実装しやすくする方法はありますか?
作成したフックをライブラリに統合するのと同じように、後でフックを追加する必要はありませんか?

プルリクエストを作成して、そこで話し合うことができると思います。

@dafflはい、特にWSに同意します。 また、クライアントとバックエンドの両方があなたまたはあなたのチームによって構築されている場合は、JWTを使用して、アプリケーションの余分な依存関係や複雑さを回避するのが最善です。
ただし、場合によっては、たとえばサードパーティ企業/開発者が使用するストアフロントREST-APIを構築する場合、通常のセッションを使用し、開発者にリクエストにクレデンシャルを含めてから、アクセスを取得する方法(および更新する方法)を説明するように依頼する方が簡単です。 )トークンを保存し、リクエストごとに渡します。 これはfeathersクライアントによって完全に処理されますが、ほとんどの場合、開発者はバックエンドの構築方法に精通していないため、 request, superagent, fetch or axiosを使用してアプリケーションをバックエンドに接続します。 少なくとも私の場合、これがAPIのストアフロント部分をJWTではなく通常のセッションで動作するように移動する主な理由でした。

しかし、この決定をコミュニティに「強制」するのではなく、独自のAPIに実装してからこの機能を適切に文書化することは、前述のストアフロントのメーカーの設計上の決定と責任ではないでしょうか。

@TheSindingユーザーセッションを管理するために最も一般的に使用されている(そして現在も使用されている)Cookieについてではなく、あまり一般的に使用されていないアプローチについて「強制」と言えると思います。

そして、はい、それはAPIを使用している開発者のフィードバックの後になされた設計上の決定です。 複数のチームで使用されるマルチテナントシステムまたはAPIを実行している場合、特に代替ソリューションがエンドユーザーに利点をもたらさない場合は、一般的な業界慣行に従って、開発者がさらに混乱したり余分な時間を費やしたりしないようにするのが最善の場合があります。

繰り返しになりますが、JWTの使用は素晴らしく、特にリアルタイムAPIの場合ははるかに簡単に利用できます。これは、90%のケースで使用されているものであり、Cookieセッションを管理するためにredisなどを実行する必要はありません。
しかし、それが最善の選択ではないかもしれないいくつかの例外的なケースがあります、私は私の前のコメントでその状況の例を持ってきました。

私はあなたが間違っていると言っていませんでした、私はただ「大声で」考えていました:)

IMO、JWTを使用したアプローチの方が優れていると思います。これは、すでにサポートされている方法であり、 操作するのも簡単だからです。

すべてのフィードバックをありがとう! JWTとセッションは、個別に説明できる別のトピックです。

私たち全員が同意することの1つは、アクセストークン+更新トークンは、単なるアクセストークンよりもはるかに安全なソリューションであるということです。 それは主要なインターネットの巨人によって広く採用されています。 Feathersコミュニティは、更新トークンの組み込みサポートを提供するFeathersのメインコードベースを望んでいると言っても過言ではありません。 実際、Feathersが更新トークンをサポートしていないことに最初に気付いたときは驚きました。

私はいくつかのプロジェクトでAWScognitoを使用していますが、AWS AmplifyはlocalStorageに3つのトークンを保存します:IDトークン、アクセストークン、更新トークン、Amplifyはトークン管理に関連するすべてのダーティワークを処理し、簡単でCognitoバックエンドと連携する簡単なインターフェース。 Feathersでも同様の開発経験が欲しいです。

@TheSindingこれまでの素晴らしい仕事に感謝します!

既存の認証は通常のサービスとして実装されているため、クラスを拡張し、カップルフックを追加することで、リフレッシュトークンのサポートを簡単に有効にできます。

this.hooks({ after: { create: [issueRefreshToken(), connection('login'), event('login')], remove: [logoutUser(), connection('logout'), event('logout')], patch: [refreshAccessToken()], },

CLIを使用して認証をオンにするのと同じように、更新トークンをサポートするためにCLIで同様のオプションを提供できます。 開発者は「はい」と答えるだけで、CLIは顧客の「更新トークン」サービスを自動的に作成し、デフォルトの構成ファイルで更新トークン関連の構成を更新します。 つまり、開発者にとってはターンキーソリューションのようなものです。

@daffl David、Feathersを作成してくれてありがとう! Feathersの「貢献ガイド」、「コーディングガイドライン」、「スタイルガイド」はありますか?

前に述べたように、更新トークン(またはいくつかのアイデア)を実装したPRは大歓迎です。 リンクされたリポジトリをチェックする機会がまだありませんでした。この議論はかなり長くなっています。 すべてを最新の状態に1か所にまとめておけば、作業がはるかに簡単になります。 いくつかの重要な箇条書き:

  • 更新トークンは、認証サービスを拡張することで発行できます
  • 更新トークンはlocalStorageに保存する必要があります
  • 更新トークンは取り消すことができる必要があります(したがって、https://docs.feathersjs.com/cookbook/authentication/revoke-jwt.htmlで説明されている失効メカニズムのより汎用的な実装が必要です)
  • 追加の複雑さとセットアップ(取り消されたトークンを保存するためのRedisなど)のため、機能として利用できますが、デフォルトで有効にしないでください(つまり、標準で生成されたアプリはCLIの一部にすることができますが、それに対して何かを行う前に、私はまだ衛生ベースのジェネレーターを開始する
  • 寄稿情報は寄稿者ガイドにあります

誰かがrefreshTokenを使用している場合は、フロントエンド、accessToken、および「セッション」のようなrefreshTokenを処理するライブラリを作成します。これは使いやすいです。

トークンを渡すだけで、ライブラリは期限切れになると、refreshTokenを使用して新しいacessTokenを取得しようとします。 現在、 Videskで使用されてい

リポジトリ:フロント認証ハンドラ

このページは役に立ちましたか?
0 / 5 - 0 評価