55

我正在构建一个 Rails API,并成功地为用户构建了一种使用 Omniauth Identity 进行身份验证的方法。

我们只需从客户端发布到 auth/identity/callback,传入 auth_key 和密码。
然后服务器返回一个门卫令牌,用户从那时起使用该令牌访问应用程序并识别自己。

该图说明了这一点:

客户服务器关系

我们现在想从客户端实现 Facebook 登录,但在理论上和实践上都无法实现。

在带有 Omniauth Identity 的简单 Rails 应用程序上,您只需调用 auth/facebook,但如果我们在客户端中放置一个链接,它会调用服务器,然后服务器会记录:

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

该应用程序在 Facebook 中正确设置了一个 ID 和 Secret,所以登录提示可能会返回到服务器?

尽管链接身份验证,但我感到困惑。任何帮助感激不尽!

在此处输入图像描述

4

3 回答 3

59

我发现的最好的方法(在这个问题上被困了一段时间之后)是手动做你的omniauth2(特别是在我的情况下使用卫星角度插件)......

我将讨论 Facebook 的解决方案,就像我的情况一样,但一切都适用于任何其他提供商。

首先你必须知道omniauth2是如何工作的(正如这里为人类所记录的那样)......

  1. 客户端:打开一个弹出窗口供用户进行身份验证。
  2. 客户:登录(如有必要),然后授权应用程序。
  3. 客户端:授权成功后,弹窗将重定向回您的应用。带有code (授权码)查询字符串参数

重定向返回 url 必须与您的前端应用程序 url 而不是后端 url 匹配,并且必须在您的 facebook 应用程序配置中指定

  1. 客户端:code参数发送回打开弹出窗口的父窗口。
  2. 客户端:父窗口关闭弹出窗口并向带有参数的POST请求发送。backend/auth/facebookcode
  3. 服务器:( code授权)换取access token

这里详细描述了如何codeaccess-tokenfacebook开发人员文档中交换

  1. 服务器:使用第6access-token步中检索到的检索用户信息。

  2. 瞧,您已经拥有了一个用户,您可以合并/创建帐户/与其他 oauth 提供者/等链接。但请记住,用户可以撤销一些权限(如电子邮件,facebook 支持撤销一些权限)...


(说够了,给我看一些代码)

首先,您必须将 HTTParty gem 添加到您的 Gemfile

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

我添加了这个要点,其中包含步骤(6、7 和 8)的流程,这些步骤是最有问题的步骤,几乎在任何地方都没有记录。

要点导出 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

就是这样......现在如果你对实现的内部感兴趣......这里是要点中看到的代码。(添加以供参考)随意分叉,编辑它,帮助它变得更好。

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

这是另一个为 instagram 实现 oauth 的 nodejs 教程,它帮助我了解了 oauth2 的工作原理(添加以供参考)

于 2015-05-24T02:17:08.577 回答
1

为了解决这个问题,我发现最好的资源是 satellizer github repo 中的 rails 示例应用程序:https ://github.com/sahat/satellizer/tree/master/examples/server/ruby

您的卫星发生器代码调用AuthController.authenticate方法。此方法使用每个提供者的 oauth 模型类将您收到的代码转换为访问令牌。然后在您的用户类中,您可以检索与您从提供商处获得的信息相匹配的用户。

最后,控制器方法将 jwt 令牌返回给客户端。

在我的情况下,控制器部分有点不同,因为我也使用设计进行邮件/密码身份验证,但我按原样复制 oauth 类,它就像一个魅力。

于 2016-05-12T06:32:52.413 回答
-1

与 facebook api 进行通信。我建议使用'omniauth-facebook' gem。您可以克隆此示例以了解更多信息:https ://github.com/ralphos/omniauth-facebook-example

于 2014-01-21T13:41:51.687 回答