我正在构建一个Rails API,并成功构建了一种使用Omniauth身份验证用户身份的方法.

我们只需从客户端发送到auth/identity/callback,并传递一个auth_密钥和密码

此图说明了这一点:

客户机-服务器关系

我们现在想从客户端实现Facebook登录,但在理论和实践上都有困难.

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

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

该应用程序在Facebook上设置正确,带有ID和密码,所以登录提示可能会返回到服务器?

虽然我把认证链接起来了,但我还是很困惑.感谢您的帮助!

在此处输入图像描述

推荐答案

我发现(在这个问题上被困了一段时间之后)最好的方法是手动执行omniauth2(特别是在我的例子中使用satellizer angular插件)...

我会像讨论我的情况一样讨论Facebook的解决方案,但一切都可以适用于任何其他Provider .

首先你必须知道omniauth2是如何工作的(as documented for humans here)...

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

the redirect back url must match your front-end app url not the back-end url and it must be specified in your facebook app configurations

  1. Client: code参数被发送回打开弹出窗口的父窗口.
  2. Client:父窗口关闭弹出窗口,并向backend/auth/facebook发送带有code参数的POST请求.
  3. Server: code(Authorization code)换access token

here is described in details how to exchange the 101 for an 102 from 100

  1. Server:使用在步骤6中检索到的access-token来检索用户的信息.

  2. 瞧,你已经有了一个用户,你可以合并/创建帐户/与其他oauthProvider 链接等.但请记住,用户可以撤销一些权限(比如email,facebook支持撤销一些权限)...


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

首先,您必须将HTTParty gem添加到您的gem文件中

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

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

gist导出了两种主要方法:

Omniauth::Facebook.authenticate(authorization_code)

用于向facebook验证用户身份,并返回用户信息、long_live_access_token(有效期为60天)

Omniauth::Facebook.deauthorize(access_token)

用于取消授权/撤销facebook上的访问令牌和应用程序权限...

这是用于我的特殊要求,当用户撤销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

下面是另外nodejs tutorial implementing oauth for instagram个帮助我理解oauth2是如何工作的(添加以供参考)

Ruby-on-rails相关问答推荐

DELETE_IF和REJECT的意外行为!方法

ActiveAdmin:的呈现索引表在父级的显示页中有许多资源

搜查升级到 4.0.0 和 ActionText::RichText

我如何在不在 gemfile 中的 rake 任务中要求 gem?

Ruby on Rails 让外键工作

如何在生成的 HTML 本身中显示部分名称

Rails 模型.有效吗?刷新自定义错误并错误地返回 true

Rails 3 远程表单:如何指定内容类型?

使用 Rspec 测试 Rails 3.1 可安装引擎

查找日期时间与今天匹配的记录 - Ruby on Rails

Rails:包含多态关联

jbuilder vs rails-api/active_model_serializers 用于 Rails 4 中的 JSON 处理

FactoryGirl 和 Rspec 测试中 attributes_for 的含义

在 Rails 4 中检测用户代理 - 读取 HTTP 标头

如何从 ActiveRecord 中的每个组中获取最新记录?

在 Rails/ActiveRecord 列名中使用问号字符

你如何覆盖 form_for 助手中的类名?

在范围内传递参数

Rails 3 返回 HTTP 406 Not Acceptable?

将新管理员添加到活动管理员