web-dev-qa-db-ja.com

サーバー認証後にフロントエンドにJSON Webトークンを提供するにはどうすればよいですか?

これまでのところ、サーバーレンダリングアプリのみを扱いました。ユーザーがユーザー名/パスワードまたはOAuthプロバイダー(Facebookなど)を使用してログインした後、サーバーはセッションCookieを設定しますが、関連ページにリダイレクトします。

しかし、今は、よりモダンなアプローチを使用して、ReactをフロントエンドとJSON APIバックエンドで使用して、アプリを構築しようとしています。これの標準的な選択は、JSON Webを使用することです。認証用のトークンですが、セッション/ローカルストレージまたはどこにでも保存できるように、JWTをクライアントに提供する方法を理解するのに苦労しています。

よりよく説明する例:

  1. ユーザーはリンク(_/auth/facebook_)をクリックしてFacebook経由でログインします

  2. ユーザーがリダイレクトされ、Facebookログインフォームまたは許可ダイアログ、あるいはその両方が表示されます(必要な場合)。

  3. Facebookはユーザーを_/auth/facebook/callback_にリダイレクトし、認証コードを牽引します。サーバーはこれをアクセストークンとユーザーに関するいくつかの情報と交換します

  4. サーバーは情報を使用してDBでユーザーを検索または作成し、次にユーザーデータの関連サブセット(IDなど)を含むJWTを作成します

  5. ???

この時点で、私はユーザーがReactアプリ(たとえば_/app_)と呼ばれる)のメインページにリダイレクトされるようにして、フロントエンドが引き継ぐことができるようにします。しかし、リダイレクトのためのクエリ文字列にそれを置く以外に、途中でJWTを失うことなくそれを行う(エレガントな)方法は考えられません(_/app?authtoken=..._)-それはアドレスに表示されますreplaceState()などを使用して手動で削除するまでは、少し奇妙に思えます。

実際、これが通常どのように行われるのか疑問に思っているだけで、ここで何か不足していると確信しています。サーバーは、Node(Koa with Passport))です。

編集:明確にするために、クライアントにトークンを提供するための最良の方法(保存できるように)を尋ねていますOAuthリダイレクトフローの後 Passportを使用する。

15
Inkling

私は最近、この同じ問題に遭遇し、ここでも他の場所でも解決策を見つけられなかったため、 このブログ投稿 を私の詳細な考えとともに書きました。

TL; DR:OAuthログイン/リダイレクト:

  1. JWTをCookieに保存してから、将来のステップでフロントエンドまたはサーバーに抽出します(たとえば、JSを使用してクライアントに抽出するか、サーバーにリクエストを送信します。サーバーはCookieを使用してJWTを取得し、戻ります。 JWT)。
  2. JWTをクエリ文字列(質問で提案)の一部として送り返します。
  3. サーバーレンダリングされたHTMLページを<script>タグ付け:
    1. 埋め込まれたJWTを自動的にlocalStorageに保存します
    2. その後、クライアントを好きなページに自動的にリダイレクトします。

(JWTを使用したログインは基本的に「JWTをlocalStorageに保存する」と同等であるため、私のお気に入りのオプションは#3でしたが、考慮していない欠点がある可能性があります。他の人の考えを聞いて興味がありますここに。)

お役に立てば幸いです。

8
GT2000
  1. クライアント:$ auth.authenticate( 'provider name')を介してポップアップウィンドウを開きます。
  2. クライアント:必要に応じてそのプロバイダーにサインインし、アプリケーションを承認します。
  3. クライアント:認証が成功すると、ポップアップがアプリにリダイレクトされます。 http:// localhost:30 、コード(認証コード)クエリ文字列パラメーター。
  4. クライアント:codeパラメータは、ポップアップを開いた親ウィンドウに送り返されます。
  5. クライアント:親ウィンドウがポップアップを閉じ、POSTリクエストを/ auth/providerにcodeパラメータ付きで送信します。
  6. サーバー:認証コードがアクセストークンと交換されます。
  7. サーバー:ユーザー情報は、手順6のアクセストークンを使用して取得されます。
  8. サーバー:固有のプロバイダーIDでユーザーを検索します。ユーザーが既に存在する場合は、既存のユーザーを取得します。それ以外の場合は、新しいユーザーアカウントを作成します。
  9. サーバー:ステップ8のどちらの場合も、JSON Webトークンを作成してクライアントに送り返します。
  10. クライアント:トークンを解析してローカルストレージに保存し、ページの再読み込み後に使用できるようにします。

    ログアウト

  11. クライアント:ローカルストレージからトークンを削除する
0
sabarinathan u

これはサーバー側からのログイン要求です。ヘッダーにトークンを格納しています:

router.post('/api/users/login', function (req, res) {
  var body = _.pick(req.body, 'username', 'password');
  var userInfo;

models.User.authenticate(body).then(function (user) {
      var token = user.generateToken('authentication');
      userInfo = user;

      return models.Token.create({
        token: token
      });
    }).then(function (tokenInstance) {
      res.header('Auth', tokenInstance.get('token')).json(userInfo.toPublicJSON());
    }).catch(function () {
      res.status(401).send();
    });
});

これは、反応側のログインリクエストです。ユーザー名とパスワードが認証に合格すると、ヘッダーからトークンを取得し、ローカルストレージにトークンを設定します。

handleNewData (creds) {
    const { authenticated } = this.state;
    const loginUser = {
        username: creds.username,
        password: creds.password
    }
    fetch('/api/users/login', {
        method: 'post',
        body: JSON.stringify(loginUser),
        headers: {
            'Authorization': 'Basic'+btoa('username:password'),
            'content-type': 'application/json',
            'accept': 'application/json'
        },
        credentials: 'include'
    }).then((response) => {
        if (response.statusText === "OK"){
            localStorage.setItem('token', response.headers.get('Auth'));
            browserHistory.Push('route');
            response.json();
        } else {
            alert ('Incorrect Login Credentials');
        }
    })
}
0
jared thomas