passport.js RESTful認証


155

WebインターフェースではなくRESTful APIを介して、passport.jsを使用して認証(ローカルやFacebookなど)をどのように処理しますか?

具体的な懸念事項は、コールバックからRESTful応答(JSON)へのデータの受け渡しの処理と、通常のres.send({data:req.data})の使用、Facebookにリダイレクトする最初の/ loginエンドポイントの設定(/ loginはJSON応答ではないため、AJAX経由でアクセスされます-これは、コールバックを使用したFacebookへのリダイレクトです)。

https://github.com/halrobertson/test-restify-passport-facebookを見つけましたが、理解できません。

さらに、passport.jsはどのように認証資格情報を保存しますか?サーバー(またはサービスですか?)はMongoDBによってサポートされており、資格情報(ログインとpwのソルトハッシュ)がそこに格納されることを期待しますが、passport.jsにこのタイプの機能があるかどうかはわかりません。


Nodeは初めてなので、簡単に始めて、のサンプルアプリケーションを確認しくださいpassport-facebook。これが機能するようになったら、次のステップは、Passportがどのように機能するか、およびPassportがどのように資格情報を保管するかを理解することです。それをRestifyに接続する(ここで言及した更新バージョンについてはこちら参照)は、最後のステップの1つになります(またはExpressにRESTインターフェースを実装することもできます)。
robertklep 2013年

回答:


312

ここでは多くの質問があり、Nodeとpassport.jsのコンテキストで質問されたとしても、実際の質問は、特定のテクノロジーでこれを行う方法よりもワークフローに関するもののようです。

@Keithの設定例を使用して、セキュリティを強化するために少し変更します。

  • のWebサーバーhttps://example.comは、単一ページのJavaScriptクライアントアプリを提供します
  • のRESTful Webサービスはhttps://example.com/api、リッチクライアントアプリにサーバーサポートを提供します
  • Nodeとpassport.jsに実装されたサーバー。
  • サーバーには、「ユーザー」テーブルを備えたデータベース(あらゆる種類)があります。
  • ユーザー名/パスワードとFacebook Connectが認証オプションとして提供されています
  • リッチクライアントはRESTリクエストを https://example.com/api
  • のWebサービスを使用してhttps://example.com/apiいるが、のWebサーバーについて知らない他のクライアント(電話アプリなど)が存在する場合がありますhttps://example.com

安全なHTTPを使用していることに注意してください。私の意見では、パスワードや認証トークンなどの機密情報がクライアントとサーバー間でやり取りされるため、公開されているすべてのサービスで必要なものです。

ユーザー名/パスワード認証

まず、単純な古い認証がどのように機能するかを見てみましょう。

  • ユーザーが接続する https://example.com
  • サーバーは、初期ページをレンダリングする豊富なJavascriptアプリケーションを提供します。ページにログインフォームがあります。
  • ユーザーがログインしていないため、この単一ページアプリの多くのセクションにはデータが入力されていません。これらすべてのセクションには、「ログイン」イベントのイベントリスナーがあります。これはすべてクライアント側のものであり、サーバーはこれらのイベントを認識していません。
  • ユーザーは自分のログイン名とパスワードを入力して送信ボタンを押すと、JavaScriptハンドラーがトリガーされ、ユーザー名とパスワードがクライアント側の変数に記録されます。次に、このハンドラーが「ログイン」イベントをトリガーします。繰り返しますが、これはすべてクライアント側のアクションであり、資格情報はまだサーバーに送信されていません
  • 「ログイン」イベントのリスナーが呼び出されます。これらのそれぞれはhttps://example.com/api、ページにレンダリングするユーザー固有のデータを取得するために、1つ以上のリクエストをRESTful APIに送信する必要があります。RESTfulであるサービスは、あるリクエストから次のリクエストまでクライアントの状態を維持できないため、Webサービスに送信するすべてのリクエストには、おそらくHTTP 基本認証の形式でユーザー名とパスワードが含まれます。Webサービスは安全なHTTPを使用しているため、パスワードは転送中に安全に暗号化されます。
  • のWebサービスはhttps://example.com/api、それぞれが認証情報を含む個々の要求の束を受け取ります。各リクエストのユーザー名とパスワードはユーザーデータベースと照合され、正しい場合はリクエストされた関数が実行され、データがJSON形式でクライアントに返されます。ユーザー名とパスワードが一致しない場合、エラーが401 HTTPエラーコードの形式でクライアントに送信されます。
  • すべてのリクエストでクライアントにユーザー名とパスワードを送信させる代わりに、RESTfulサービスで「get_access_token」関数を使用して、ユーザー名とパスワードを取得し、トークンで応答することができます。関連付けられた日付。これらのトークンは、ユーザーごとにデータベースに格納されます。その後、クライアントは後続のリクエストでアクセストークンを送信します。アクセストークンは、ユーザー名とパスワードの代わりにデータベースに対して検証されます。
  • 電話アプリなどの非ブラウザークライアントアプリケーションは、上記と同じように動作し、ユーザーに資格情報を入力するように求め、Webサービスへのすべてのリクエストでそれら(またはそれらから生成されたアクセストークン)を送信します。

この例の重要なポイントは、RESTful Webサービスはすべてのリクエストで認証を必要とすることです

このシナリオでは、追加のセキュリティ層により、ユーザー認証に加えてクライアントアプリケーションの承認が追加されます。たとえば、すべてウェブサービスを使用するウェブクライアント、iOSアプリ、Androidアプリがある場合、認証されたユーザーが誰であるかに関係なく、特定のリクエストのクライアントが3つのうちどれであるかをサーバーに認識させることができます。これにより、Webサービスで特定の機能を特定のクライアントに制限できます。これについては、APIキーとシークレットを使用できます。いくつかのアイデアについては、この回答を参照してください。

Facebook認証

上記のワークフローはFacebook接続では機能しません。Facebook経由のログインにはサードパーティのFacebook自体があるためです。ログイン手順では、ユーザーがFacebookのWebサイトにリダイレクトされ、ここで資格情報が私たちの管理外で入力されます。

それでは、状況がどのように変化するかを見てみましょう。

  • ユーザーが接続する https://example.com
  • サーバーは、初期ページをレンダリングする豊富なJavascriptアプリケーションを提供します。このページには、「Facebookでログイン」ボタンを含むログインフォームがある場合があります。
  • ユーザーが[Facebookでログイン]ボタンをクリックすると、(たとえば)にリダイレクトされるだけのリンクになりhttps://example.com/auth/facebookます。
  • https://example.com/auth/facebookルートがpassport.jsによって処理され(参照マニュアルを参照して
  • ユーザーが目にするのは、ページが変更され、Facebookがホストするページに移動して、ログインしてWebアプリケーションを承認する必要があることだけです。これは完全に私たちの制御外です。
  • ユーザーのFacebookにログインしてFacebookは今、私たちは次の例passport.jsセットアップ、で構成されていること、バックコールバックURLにリダイレクトして、我々のアプリケーションに許可を与える文書れますhttps://example.com/auth/facebook/callback
  • https://example.com/auth/facebook/callbackルートのpassport.jsハンドラーは、Facebookのアクセストークンと、ユーザーの電子メールアドレスを含むFacebookからのユーザー情報を受信するコールバック関数を呼び出します。
  • 電子メールを使用して、データベースでユーザーを特定し、Facebookアクセストークンを保存できます。
  • Facebookコールバックで最後に行うことは、リッチクライアントアプリケーションにリダイレクトすることですが、今回は、ユーザー名とアクセストークンをクライアントに渡して、それらを使用できるようにする必要があります。これは、いくつかの方法で実行できます。たとえば、JavaScript変数をサーバー側のテンプレートエンジンを介してページに追加したり、この情報とともにCookieを返すことができます。(最初に提案したように、URLでこのデータを渡すことによるセキュリティの問題を指摘してくれた@RyanKimberに感謝します)。
  • これで、もう一度シングルページアプリを起動しますが、クライアントにはユーザー名とアクセストークンがあります。
  • クライアントアプリケーションは、「ログイン」イベントをすぐにトリガーし、アプリケーションのさまざまな部分に、Webサービスから必要な情報を要求させることができます。
  • に送信さhttps://example.com/apiれるすべてのリクエストには、認証のためのFacebookアクセストークン、またはREST APIの「get_access_token」関数を介してFacebookのトークンから生成されたアプリケーション独自のアクセストークンが含まれます。
  • OAuthではログインにWebブラウザーが必要なため、ブラウザー以外のアプリでは少し難しいです。電話またはデスクトップアプリからログインするには、ブラウザーを起動してFacebookにリダイレクトする必要があります。さらに悪いことに、ブラウザがFacebookアクセストークンを何らかのメカニズムを介してアプリケーションに返す方法が必要です。

これでほとんどの質問に答えられるといいのですが。もちろん、FacebookをTwitter、Google、またはその他のOAuthベースの認証サービスに置き換えることができます。

これに対処する簡単な方法があるかどうか知りたいです。


5
細かい対応ありがとうございます。ただ1つの質問です。あなたはそれを言いEvery single request they send to the web service will include the username and password、それでもあなたは言いyou can have a "get_access_token" function in your RESTful serviceます。RESTはステートレスである必要があると言うのは矛盾しているようですが、アクセストークンを保存するという行為はサーバーが現在ステートフルであることを意味するため、サーバー側でのアクセストークンの保存は問題ありません。これについての説明や正当化をお願いします。ありがとう!:)
ライアンヒ2013年

6
ステートレス要件について考えるとき、クライアントとの特定のセッションの状態について考えます。パスワードやアクセストークンなど、ユーザーに関連付けられたデータをデータベースに保存することは、少なくとも「セッションステート」ではなく「ステート」であるとは思いません。サーバーは明らかにユーザーとパスワードを知っている必要がありますが、これはセッションに関連付けられていないアプリケーションデータです。とはいえ、RESTfulと思われるサービスで多くの人がCookieを使用しているため、REST仕様にどれだけ厳密に準拠するかは、実際には各実装者次第です。
ミゲル

1
@Dexter:従来のログインの場合、ユーザーはフォームにユーザー名とパスワードを入力し、[送信]ボタンをクリックすると、この情報がWebサーバーに投稿されます。この場合、これは発生せず、ユーザーはフォームに入力し、送信時にJavaScriptハンドラー(送信ボタンのonClickイベント)を押すと、データをキャプチャしてクライアントコンテキストに保持します。表示する準備ができている例はありませんが、このチュートリアルの2番目の部分に注意して、ブログでその方法を示します。blog.miguelgrinberg.com
Miguel

2
これは非常によく考えられた記事ですが、1つの重要な見落としやエラーが含まれています。Facebookログイン(またはGithub、twitterなど)を処理するときは、トークンをCookieでクライアントに返し、発見されたらクライアント側でCookieを削除することをお勧めします。トークンをURL文字列の一部として渡すと、このURLがブラウザーの履歴に追加され、(処理が不適切な場合)ブラウザーによってこのURLが要求される可能性があります。どちらもトークンを表示します。URLをコピーしたユーザーは、アプリケーションでこのユーザーを偽装する可能性があります。
Ryan Kimber 2014年

1
@Nathan:https経由の基本認証は安全であるため、はい、それは許容できるメカニズムです。
ミゲル

11

@Miguelの説明はすべての場合の完全なフローで非常に感謝していますが、Facebook認証の部分にいくつか追加したいと思います。

Facebookは、クライアント側のアクセストークンを直接取得するために使用できるJavascript SDKを提供します。これは、サーバーに渡され、さらにすべてのユーザー情報をFacebookからプルするために使用されます。したがって、基本的にリダイレクトは必要ありません。

さらに、モバイルアプリケーションにも同じAPIエンドポイントを使用できます。FacebookのAndroid / iOS SDKを使用し、クライアント側でFacebookのaccess_tokenを取得してサーバーに渡します。

説明されているステートレスの性質に関して、get_access_tokenを使用してトークンを生成し、クライアントに渡すと、このトークンもサーバーに保存されます。だからそれはセッショントークンと同じくらい良いです、そしてこれはそれをステートフルにすると思いますか?

ちょうど私の2セント..


1
この方法でFacebookを使用して1ページのみのAjax認証を実行できるため、これは非常に重要です。トークンはセッション認証と同等に安全です。唯一の違いは、トークンを別のドメインに渡すことができ、セッションは特定の1つのドメインでのみ使用されることです。expressjsとpassportを使用すると、状態を保存するAPIを作成し、セッション認証を利用できます。
jperelli 14

Facebookに対してアクションを実行するためにユーザーを認証したい場合、JavaScript APIは素晴らしいですが、サーバー/データベースに対してユーザーを検証したい場合、それだけでは役に立たないのです。
James Westgate

4
上記のMiguelで説明されている方法を使用することもできますが、認証コールバックでクライアントをリダイレクトするときに、独自のJWTトークンをCookieとして発行します。これにより、両方の長所が得られます。シングルページアプリケーションは、同じレベルのセキュリティを維持しながら、1種類の認証(JWT)のみを考慮でき、ソーシャルログインをサポートする柔軟性を提供します。各ソーシャルネットワーク(Facebook、Twitter、LinkedIn、Googleなど)に固有のJavaScript API。また、ユーザー名/パスワードおよびRESTアクセスのAJAXスタイルのサポートを維持することもできます。
Ryan Kimber 2014年

Facebook JavaScript SDKは現在Chrome iOSでは動作しません。たぶんいくつかの問題。
demisx 2015年

@RyanKimberは、これが例として完全に行き詰まっている例として、小さなgitリポジトリなどを作成できます
SimonDragsbækJun

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.