web-dev-qa-db-ja.com

Facebookユーザーのアクセストークンは、サーバー側でどのように使用する必要がありますか?

序文

私はいくつかのWebサービスと、HTTPを介してこれらのサービスとインターフェイスするいくつかのクライアント(Webアプリ、モバイルなど)を開発しています。私の現在の作業項目は、製品の認証および承認ソリューションを設計することです。 Facebook、Google、Microsoft、Twitterなどの外部IDプロバイダーを認証に活用することにしました。

「リクエストがサーバーに届いたとき、ユーザーが誰であるか、どうすれば確認できるか」という問題を解決しようとしています。以下の質問も...

要件

  1. 外部IDに依存して、私が誰を扱っているかを示します(「userId」は本質的に私が気にするすべてです)。
  2. システムは、トークンベースの認証を使用する必要があります(Cookieや基本認証とは対照的に)。

    これは、疎結合を提供しながら、複数のクライアントとサーバー間でスケーリングするための正しい選択だと思います。

ワークフロー

トークンベースの認証に関する私の読み物と理解に基づいて、次のようにワークフローを想像します。ここでは、WebブラウザーでのFacebookに注目しましょう。私はまだ確認していませんが、他の外部IDプロバイダーには同様の機能があるはずだと思います。

この記事を書いている時点では、Facebookログインバージョン2.2に基づいていることに注意してください。

  1. Client:JavaScript SDK を使用してFacebookへのログインを開始します
  2. Facebook:ユーザーはアプリの権限を認証および承認します(たとえば、ユーザーのパブリックプロファイルにアクセスするため)
  3. Facebook:ユーザーのアクセストークン、ID、署名付きリクエストを含む応答をクライアントに送信します
  4. Client:ブラウザセッションにユーザーアクセストークンを保存します( SDKで便利に処理されます
  5. Client:認証ヘッダー+ユーザーID(カスタムヘッダー内)のユーザーのアクセストークンを一緒に送信することにより、Webサービスに安全なリソースを要求します潜在的に)
  6. Server:リクエストヘッダーからユーザーアクセストークンを読み取り、Facebookが提供するdebug_token graph APIにリクエストを送信して検証を開始します
  7. Facebook:ユーザーアクセストークン情報(appIdおよびuserIdを含む)でサーバーに応答します。
  8. Server:appIdを予想されるもの(それ自体に既知)と比較し、userIdをクライアント要求で送信されたものと比較することにより、トークンの検証を完了します
  9. Server:要求されたリソースでクライアントに応答します(正常な認証パスを想定)

サーバーへの後続のリクエストに対してステップ5〜9が繰り返されることを想像します(ユーザーのアクセストークンが有効である間-有効期限が切れていない、FB側から取り消されている、アプリの権限が変更されているなど)

手順を説明する図を次に示します。このシステムはnot単一ページアプリケーション(SPA)ではないことを理解してください。上記のWebサービスは、基本的にクライアントにJSONデータを提供するAPIエンドポイントです。 HTML/JS/CSSを提供していません(Webクライアントサーバーを除く)。

Workflow diagram

質問

  1. 何よりもまず、私の序文と要件に基づいて、説明されたアプローチで目立ったギャップ/落とし穴がありますか?

  2. アクセストークンを確認するためにFacebookへのアウトバウンドリクエストを実行していますか(上記の手順6〜8)、クライアントリクエストごとに必要/推奨ですか?

    少なくとも、クライアントリクエストからのアクセストークンを確認する必要があります。ただし、最初の検証後の検証で推奨されるアプローチは私には不明です。典型的なパターンがある場合、私はそれらについて聞いてみたいです。私の要件に基づいて、アプリケーションに依存する可能性があることを理解しています。ただし、何を探すべきかまだわかりません。基本的な考え方ができたら、デューデリジェンスを行います。

    たとえば、考えられること:

    • 最初の検証が完了した後、アクセストークンとユーザーIDのペアをハッシュし、アクセストークンと同等の有効期限で分散キャッシュ(すべてのWebサーバーからアクセス可能)に保存します。クライアントからの後続の要求に応じて、アクセストークンとuserIdのペアをハッシュし、キャッシュ内の存在を確認します。存在する場合、リクエストは承認されます。それ以外の場合は、FacebookグラフAPIにアクセスしてアクセストークンを確認します。 HTTPS(これから使用します)を使用している場合、この戦略は実行可能であると考えています。ただし、パフォーマンスはどのように比較されますか?

    • このStackOverflowの質問 で受け入れられた回答では、Facebookユーザートークンの最初の検証が完了した後にカスタムアクセストークンを作成することを推奨しています。その後、カスタムトークンは、後続の要求のためにクライアントに送信されます。しかし、これが上記のソリューションよりも複雑かどうか疑問に思っています。これには、独自のIDプロバイダーを実装する必要があります(最初は外部IDプロバイダーを使用するため、避けたいものです...)。この提案にメリットはありますか?

  3. 上記のステップ3の応答にsignedRequestフィールドがあります(前述の here )。これは、「ゲームキャンバスログイン」フローの署名済みリクエストパラメーター here と同等ですか?

    前者はドキュメントの後者にリンクしているため、それらは同等であると示唆されているようです。ただし、ゲームのページに記載されている検証戦略が、Webドキュメントの「ログインフローを手動で構築する」 page に記載されていないことに驚いています。

  4. #3の答えが「はい」の場合、署名をデコードし、サーバー側で使用されると予想されるものと比較する同じID確認戦略を使用できますか?

    Decode & compare from FB docs

    Debug_token graph APIのアウトバウンドコール(上記の手順6)を行う代わりに、これを活用して推奨されるアクセストークンを確認できるかどうか疑問に思っています here

    debug_token graph API from FB docs

    もちろん、サーバー側で比較を行うには、署名されたリクエスト部分をリクエストとともにサーバーに送信する必要があります(上記のステップ#5)。セキュリティを犠牲にすることなく実現可能性に加えて、私は、パフォーマンスがアウトバウンドコールを行うことと比較してどうなるかと思っています。

  5. 私がそれに取り組んでいる間、どのようなシナリオで/どのような目的で、たとえばユーザーのアクセストークンをデータベースに保持しますか?これを行う必要があるシナリオは表示されませんが、何かを見落としている可能性があります。私は、sparkいくつかの考えに対する一般的なシナリオがいくつかあるのではないかと思っています。

ありがとう!

64
Scott Lin

あなたが説明したことから、サーバーサイドのログインフローを使用することをお勧めします

トークンは既にサーバー上にあり、クライアントから渡す必要はありません。暗号化されていない接続を使用している場合、これはセキュリティリスクになる可能性があります(たとえば、中間者攻撃の場合)。

手順は次のとおりです:

(1)ユーザーのログイン

scopeパラメーターでユーザーから収集するアクセス許可を指定する必要があります。リクエストは通常​​のリンクを介してトリガーできます:

GET https://www.facebook.com/dialog/oauth?
    client_id={app-id}
   &redirect_uri={redirect-uri}
   &response_type=code
   &scope={permission_list}

見る

(2)アイデンティティを確認する

GET https://graph.facebook.com/oauth/access_token?
    client_id={app-id}
   &redirect_uri={redirect-uri}
   &client_secret={app-secret}
   &code={code-parameter}

(3)アクセストークンを検査する

質問で既に言ったようにトークンを調べることができます

GET /debug_token?input_token={token-to-inspect}
    &access_token={app-token-or-admin-token}

これはサーバー側でのみ行う必要があります。そうしないと、アプリのアクセストークンをエンドユーザーに表示できるようになるためです(お勧めしません)。

見る

(4)アクセストークンの拡張

(短命の)トークンを取得したら、以下の説明に従ってトークンを拡張する呼び出しを行うことができます。

次のように:

GET /oauth/access_token?grant_type=fb_exchange_token
    &client_id={app-id}
    &client_secret={app-secret}
    &fb_exchange_token={short-lived-token}

(5)アクセストークンの保存

サーバーへのトークンの保存に関して、FBはそうすることを提案します。

(6)期限切れのアクセストークンの処理

FBはトークンの有効期限が切れた場合に通知しないため(有効期限を保存せず、呼び出しを行う前に現在のタイムスタンプと比較しない場合)、トークンが無効になった場合、FBからエラーメッセージを受け取る可能性があります(最大60日後)。エラーコードは190

{
  "error": {
    "message": "Error validating access token: Session has expired at unix 
                time SOME_TIME. The current unix time is SOME_TIME.", 
    "type": "OAuthException", 
    "code": 190
  }
}

見る

アクセストークンが無効になった場合の解決策は、ユーザーに再度ログインしてもらうことです。この時点で、再度ユーザーに代わってAPI呼び出しを行うことができます。アプリが新しい人に使用するログインフローによって、採用する必要がある方法が決まります。

23
Tobi
  1. 目立った隙間や落とし穴は見当たりませんが、セキュリティの専門家ではありません。
  2. サーバーが指定されたトークンを検証したら(ステップ8)、次のようになります。

このStackOverflowの質問で受け入れられている回答では、Facebookユーザートークンの最初の検証が完了した後にカスタムアクセストークンを作成することを推奨しています。その後、カスタムトークンは、後続の要求のためにクライアントに送信されます。しかし、これが上記のソリューションよりも複雑かどうか疑問に思っています。これには、独自のIDプロバイダーを実装する必要があります(最初は外部IDプロバイダーを使用するため、避けたいものです...)。この提案にメリットはありますか?

私見は行く方法です。 https://jwt.io/ を使用します。これにより、秘密鍵を使用して値(たとえば、userId)をエンコードできます。次に、クライアントはこのトークンをすべてのリクエストに添付します。そのため、第三者に依頼せずにリクエストを検証できます(データベースクエリも必要ありません)。ここでの良いところは、トークンをDBに保存する必要がないことです。

トークンに有効期限を定義して、必要に応じてクライアントにサードパーティによる再認証を強制することができます。

  1. サーバーがクライアントとの対話なしで何らかのアクションを実行できるようにしたいとしましょう。例: グラフストーリーを開く 。このシナリオでは、ユーザーの名前で何かを公開する必要があるため、DBに保存されているアクセストークンが必要になります。

(3と4の質問には対応できません、申し訳ありません)。

1
ilopezluna