web-dev-qa-db-ja.com

passport.jsとexpress.js(node.js)を使用して安全なoauth APIを作成する)

私は正しい方法で物事をしていない限り、私は特定の問題を考える必要があります。

クライアント(htmlサイト)とAPI(express + mongodbで構築)の2つの側面を持つアプリケーションがあります。 APIに安全にアクセスする必要があります。どちらも異なるドメインにあります。今のところ、私のサイトがdomain.comにあり、私のAPIがapi.com:3000にあるとします。

データを使用してユーザーを作成しているときに、Githubからアクセストークンを取得するためのパスポートを追加したので、基本的に「Githubでサインイン」できます。

私の現在のプロセスは次のとおりです。

1)クライアント(サイト)は、APIでポップアップを開きます

window.open("http://api.com:3000/oauth")

2)エクスプレスサーバーで、パスポートプロセスを開始します。

app.get('/oauth', passport.authenticate('github'), function(req, res) {

});

戦略には このコード を使用しました。

3)コールバックをポップアップを閉じるURLにリダイレクトします(window.close()を使用したjavascript):

app.get('/oauth/callback', passport.authenticate('github', { failureRedirect: '/' }), function(req, res) {
    res.redirect('/auth_close');
});

その時点で、すべて問題なく、APIにログインしています。私の問題は、クライアントがaccessToken、ユーザー、またはユーザーIDについてまだ何も知らないため、データをクライアントに戻す方法です。

そこで、クライアント(ポップアップを開くサイト)から、さまざまなアプローチを試しました。

  • ポップアップから値を取得する:リダイレクトが原因で機能しない、ポップアップのJavaScript情報を追跡できなくなる

  • aPIを呼び出して、セッションで「現在のユーザー」を取得します。たとえば、 http://api.com:3000/current もう1つのリクエストは理想的ではありませんが、これは機能します。しかし、まだ問題があります。

この/ current urlは、ブラウザーから到達した場合にユーザーを返します。これは、ブラウザーがヘッダー要求でエクスプレスセッションCookieを送信するためです。

Cookie:connect.sid=s%3AmDrM5MRA34UuNZqiL%2BV7TMv6.Paw6O%2BtnxQRo0vYNKrher026CIAnNsHn4OJdptb8KqE

問題は、jqueryなどからこのリクエストを行う必要があることです。セッションが送信されないため、ここで失敗します。したがって、ユーザーは返されません。

app.get('/current', function() {
    // PROBLEM HERE, req.user is undefined with ajax calls because of the session!
});

私はそれを機能させる方法を見つけましたが、クロスブラウザのCORSの問題が発生するため、満足していません。

res.header("Access-Control-Allow-Credentials", "true");

そして、説明されているように、jqueryajax呼び出しにwithCredentialsフィールドを追加します ここ

ネイティブXHRオブジェクトに設定するfieldNameとfieldValueのペアのマップ。たとえば、必要に応じて、クロスドメインリクエストのwithCredentialsをtrueに設定するために使用できます。

$.ajax({
   url: a_cross_domain_url,
   xhrFields: {
      withCredentials: true
   }
});

ヘッダーのワイルドカードが失われているので、これにも満足していません:Access-Control-Allow-Origin、説明されているように、ドメインを指定する必要があります ここ

したがって、ここで取るべきアプローチがわかりません。必要なのは、クライアントがパスポートoauthプロセスからaccessTokenまたはuserIDを取得することだけです。アイデアは呼び出しを検証するためのAPIへの各呼び出しのトークン。

どんな手掛かり?

18
Soundstep

私も同じ問題を抱えていました。ログインページでローカル戦略を使用していて、ユーザーが他のリクエストでセッションに参加しているかどうかを確認していました。

あなたが言うように、解決策は、XMLHTTPRequestを使用してセッションIDをCookieで渡すためにCORSを使用することです。

すべてのブラウザでまだ機能していないCORSを使用する代わりに、他のリクエストでアクセストークンを使用することにしました。私が使用したワークフローは次のとおりです。

POST /login
  • ユーザー名とパスワードは本文に渡されます。
  • ローカル戦略を使用した認証。
  • 応答は、access_tokenを含むユーザーオブジェクトを返します

GET/endpoint/abc123?access_token = abcdefg

  • ログイン応答から取得したトークン
  • Bearer Strategyを使用した認証(パスポート-http-bearer)

Expressではセッションは不要になりました。

この代替案がお役に立てば幸いです。

13
Scott Switzer

Expressアプリで何かを構成する前に、以下を使用して(まったく同じ)、クロスドメインの応答のヘッダーを設定します。

app.use(function(req, res, next) {
res.header('Access-Control-Allow-Credentials', true);
res.header('Access-Control-Allow-Origin', req.headers.Origin);
res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE');
res.header('Access-Control-Allow-Headers', 'X-Requested-With, X-HTTP-Method-Override, Content-Type, Accept');
if ('OPTIONS' == req.method) {
     res.send(200);
 } else {
     next();
 }
});
1
sam100rav