web-dev-qa-db-ja.com

Rails API?のomniauthとFacebookを使用してユーザーを認証します

Rails APIを構築しており、ユーザーがOmniauth Identityを使用して認証する方法を正常に構築しました。

クライアントから単にauth/identity/callbackに投稿し、auth_keyとパスワードを渡します。
その後、サーバーはドアキーパートークンを返します。このトークンはその後、ユーザーがアプリにアクセスして自分自身を識別するために使用します。

この図はこれを示しています:

Client server relationship

クライアントからFacebookログインを実装したいのですが、理論的にも実際的にも、それを機能させるのに苦労しています。

単純なRails Omniauth Identityのアプリでは、auth/facebookを呼び出すだけですが、これにリンクをクライアントに配置すると、サーバーが呼び出され、サーバーがログに記録します。

INFO -- omniauth: (facebook) Request phase initiated.

アプリはFacebookでIDとシークレットを使用して正しくセットアップされているので、ログインプロンプトがサーバーに返されるのでしょうか。

認証を連鎖させても混乱しています。どんな助けもありがたいことに感謝します!

enter image description here

46
idrysdale

私が見つけた最良の方法は(この問題でしばらくの間立ち往生した後)、あなたのomniauth2を行うことです(特に私の場合は satellizer angular plugin)を使用して手動で。 。

私の場合はFacebookのソリューションについて説明しますが、他のプロバイダーにもすべて適用できます。

最初にomniauth2がどのように機能するかを知る必要があります( ここで人間のために文書化されています )...

  1. Client:ユーザーが認証するためのポップアップウィンドウを開きます。
  2. Client:サインイン(必要な場合)してから、アプリケーションを認証します。
  3. Client:認証が成功すると、ポップアップがアプリにリダイレクトされます。 code(認証コード)クエリ文字列パラメーター

リダイレクトバックURLは、バックエンドURLではなく、フロントエンドアプリのURLと一致する必要があり、Facebookアプリの構成で指定する必要があります

  1. Client:codeパラメータは、ポップアップを開いた親ウィンドウに送り返されます。
  2. Client:親ウィンドウはポップアップを閉じ、POSTパラメーターを使用してcode要求をbackend/auth/facebookに送信します。
  3. サーバー:code認証コード)が交換されます access token

ここで、 facebook developer documentationからcodeaccess-tokenに交換する方法を詳細に説明します。

  1. Server:ステップ6で取得したaccess-tokenを使用ユーザーの情報を取得します。

  2. VOILAあなたは自分自身に他のoauthプロバイダーなどとアカウントをマージ/作成/リンクすることができますが、ユーザーはemail、facebookは許可の一部の取り消しをサポートしています)...


(話して、コードを見せてください)

まず、GemfileにHTTParty gemを追加する必要があります

gem 'httparty'  # Makes http fun again (http client)

this Gist を追加しました。これには、ステップ(6、7、および8)のフローが含まれます。これらは最も問題のあるステップであり、ほとんどどこにも文書化されていません。

gistは2つの主な方法をエクスポートします。

Omniauth::Facebook.authenticate(authorization_code)

facebookでユーザーを認証し、user_info、long_live_access_tokenを返すために使用されます(60日間有効)

Omniauth::Facebook.deauthorize(access_token)

これは、facebookのaccess_tokenおよびアプリケーション許可の認証解除/取り消しに使用されます...

これは、ユーザーがfacebookログインで要求された電子メール許可を取り消すときの特別な要件に使用されます...アプリケーション全体の許可を取り消します...これにより、ユーザーは最初のログインであるかのように次のログインでプロンプトが表示されます(不要) Facebookアプリに移動して、アプリケーションを手動で取り消すには)...

コントローラーでの使用方法は次のとおりです

user_info, access_token = Omniauth::Facebook.authenticate(params['code'])
if user_info['email'].blank?
  Omniauth::Facebook.deauthorize(access_token)
end

これで...実装の内部に興味があるなら... Gistに見られるコードはここにあります。 (参照用に追加)自由にフォーク、編集、改善を支援してください。

require 'httparty'

module Omniauth
  class Facebook
    include HTTParty

    # The base uri for facebook graph API
    base_uri 'https://graph.facebook.com/v2.3'

    # Used to authenticate app with facebook user
    # Usage
    #   Omniauth::Facebook.authenticate('authorization_code')
    # Flow
    #   Retrieve access_token from authorization_code
    #   Retrieve User_Info hash from access_token
    def self.authenticate(code)
      provider = self.new
      access_token = provider.get_access_token(code)
      user_info    = provider.get_user_profile(access_token)
      return user_info, access_token
    end

    # Used to revoke the application permissions and login if a user
    # revoked some of the mandatory permissions required by the application
    # like the email
    # Usage
    #    Omniauth::Facebook.deauthorize(access_token)
    # Flow
    #   Send DELETE /me/permissions?access_token=XXX
    def self.deauthorize(access_token)
      options  = { query: { access_token: access_token } }
      response = self.delete('/me/permissions', options)

      # Something went wrong most propably beacuse of the connection.
      unless response.success?
        Rails.logger.error 'Omniauth::Facebook.deauthorize Failed'
        fail Omniauth::ResponseError, 'errors.auth.facebook.deauthorization'
      end
      response.parsed_response
    end

    def get_access_token(code)
      response = self.class.get('/oauth/access_token', query(code))

      # Something went wrong either wrong configuration or connection
      unless response.success?
        Rails.logger.error 'Omniauth::Facebook.get_access_token Failed'
        fail Omniauth::ResponseError, 'errors.auth.facebook.access_token'
      end
      response.parsed_response['access_token']
    end

    def get_user_profile(access_token)
      options = { query: { access_token: access_token } }
      response = self.class.get('/me', options)

      # Something went wrong most propably beacuse of the connection.
      unless response.success?
        Rails.logger.error 'Omniauth::Facebook.get_user_profile Failed'
        fail Omniauth::ResponseError, 'errors.auth.facebook.user_profile'
      end
      response.parsed_response
    end


    private

    # access_token required params
    # https://developers.facebook.com/docs/facebook-login/manually-build-a-login-flow/v2.3#confirm
    def query(code)
      {
        query: {
          # The authorization_code we want to exchange for the access_token
          code: code,
          # This must match the redirectUrl registerd in the facebook app.
          # You can save it to ENV['WEB_APP_URL'] if you have multiple facebook apps for development and testing
          # so you can support testing app on development and production app on production env.
          redirect_uri: "http://localhost:9000/",
          client_id: ENV['FB_APP_ID'], # Facebook appId
          client_secret: ENV['FB_APP_SECRET'], # Facebook app secret (must not exist on front-end app for security)
        }
      }
    end
  end
end

ここにもう1つあります instagramのoauthを実装するnodejsチュートリアル oauth2の動作を理解するのに役立ちました(参照用に追加)

48
a14m

この問題を解決するために私が見つけた最良のリソースは、サテライザーgithubリポジトリのRailsサンプルアプリケーション: https://github.com/sahat/satellizer/tree/master/examples/server/Ruby

サテライザーコードは AuthController.authenticate メソッドを呼び出します。このメソッドは 各プロバイダーのoauthモデルクラス を使用して、受け取ったコードをアクセストークンに変換します。次に、 ser class で、プロバイダーから取得した情報に一致するユーザーを取得できます。

最後に、コントローラーメソッドはjwtトークンをクライアントに返します。

私の場合、メール/パスワード認証にもdeviseを使用していますが、コントローラー部分は少し異なりますが、th oauthクラスをそのままコピーし、魅力のように機能します。

1
Seb Cesbron

Facebook apiとの通信へ。 'omniauth-facebook' gem を使用することをお勧めします。この例を複製して、さらに理解することができます。 https://github.com/ralphos/omniauth-facebook-example

0
khanh