简体   繁体   English

如何在设计中使用http身份验证,并使用可选的omniauth令牌作为身份验证令牌

[英]How to use http authentication in devise with an optional omniauth token as the authentication token

We have a rails app setup that uses devise & omniauth to allow logging in via facebook authentication. 我们有一个rails应用程序设置,它使用devise&omniauth允许通过facebook身份验证登录。 We also have a mobile app that is currently using http authentication to login to the rails app either by passing username & password or by passing in the http authentication token. 我们还有一个移动应用程序,当前使用http身份验证通过传递用户名和密码或传入http身份验证令牌来登录rails应用程序。 This all works great so far. 到目前为止,这一切都很有效。

The mobile app also has the ability to authenticate with facebook itself and receive the user facebook token directly between itself and facebook. 移动应用程序还具有与Facebook本身进行身份验证的能力,并直接在自己和Facebook之间接收用户facebook令牌。

I would like to bridge this gap so that if the user has logged in from the mobile app via facebook and has their facebook token, allow that facebook token to be used as the authentication on the rails app as if they had received it from facebook via the browser. 我想弥合这个差距,以便如果用户通过Facebook从移动应用程序登录并拥有他们的facebook令牌,允许将facebook令牌用作rails app上的身份验证,就好像他们已经从facebook通过浏览器。

The end result would be that the mobile app can log a user in via: 最终结果是移动应用程序可以通过以下方式登录用户:

1) username/password or 2) http authentication token or 3) omniauth (facebook) token 1)用户名/密码或2)http认证令牌或3)omniauth(facebook)令牌

Also, in the case of 3), if the user doesn't yet exist on the rails app, would need to create the user - doing that now already with browser side authentication so there may be nothing more to do. 此外,在3)的情况下,如果用户尚未存在于rails应用程序中,则需要创建用户 - 现在已经使用浏览器端身份验证执行此操作,因此可能无需执行任何操作。

How can I best accomplish this within the devise construct? 如何在设计构造中最好地实现这一目标?

Just did this, but never saw an end-to-end solution. 刚刚做到这一点,但从未见过端到端的解决方案。

This addresses point 3. 1 & 2 are easily accomplished with devise and documented elsewhere. 这解决了第3点.1和2很容易通过设计和其他地方的文档来完成。

Not too hard to add FB auth to your web app, the instructions are on github for omniauth and omniauth-facebook. 将FB身份验证添加到您的网络应用程序并不太难,指示在omithuth和omniauth-facebook的github上。

I believe the following stands alone, without doing the omniauth-facebook integration, if you want to do it that way. 如果你想这样做,我相信以下是独立的,没有进行omniauth-facebook集成。 This is similar to other approaches out there. 这类似于其他方法。 My idea was to attempt to use the devise model as closely as I could. 我的想法是尝试尽可能地使用设计模型。

You'll need the fb_graph gem. 你需要fb_graph gem。

On the mobile client, you authenticate with FB appropriately and put the returned access token in the header of your http requests. 在移动客户端上,您可以适当地使用FB进行身份验证,并将返回的访问令牌放在http请求的标头中。 I used the header fb_access_token. 我使用了标头fb_access_token。 Just like basic auth, you'll want to send this over SSL to avoid sniffing of the token. 就像基本身份验证一样,您需要通过SSL发送此信息以避免嗅探令牌。 Using the header allows me to interchange basic auth and FB auth without changing the requests, but you could use a parameter if you prefer. 使用标题允许我在不更改请求的情况下交换基本auth和FB auth,但如果您愿意,可以使用参数。

This solution implements a warden strategy which is based off of the devise Authenticatable warden strategy. 该解决方案实现了一个基于可验证的可验证监管策略的监管策略。 The difference here is the fact that this strategy utilizes an HTTP header called fb_access_token containing the facebook access token string that was retrieved using the mobile application. 这里的不同之处在于,此策略使用名为fb_access_token的HTTP标头,其中包含使用移动应用程序检索的facebook访问标记字符串。

Once you know this, the code is pretty straightforward. 一旦你知道这一点,代码就非常简单了。

In a file, in the config/initializers directory, add the following. 在文件的config / initializers目录中,添加以下内容。 I happened to call mine fb_api_strategy.rb: 我碰巧叫我的fb_api_strategy.rb:

# authentication strategy to support API authentication over the webservice
# via facebook

require 'devise/strategies/database_authenticatable'
require 'fb_graph'
require 'warden'

module Devise
  module Strategies
    class FbMobileDatabaseAuthenticatable < Authenticatable
  def valid?
    # if we have headers with the facebook access key
    !!request.headers["fb_access_token"]
  end

  def authenticate!
    token = request.headers["fb_access_token"]
    fbuser = FbGraph::User.me(token)
    fbuser = fbuser.fetch

    user = User.find_for_facebook_mobile_client(fbuser.email)

    # this either creates a new user for the valid FB account, or attaches
    # this session to an existing user that has the same email as the FB account

    if !!user && validate(user) { true }
      user.after_database_authentication
      success!(user)
    elsif !halted? || !user
      fail(:invalid)
        end
      end
    end
  end
end

Warden::Strategies.add(:fb_database_authenticatable,
                       Devise::Strategies::FbMobileDatabaseAuthenticatable)

In config/initializers, add the following to devise.rb: 在config / initializers中,将以下内容添加到devise.rb:

  config.warden do |manager|
    manager.default_strategies(:scope => :user).unshift :fb_database_authenticatable
  end

To allow you to either create a user or find an existing user based on the FB email, add the following to your user model: 要允许您根据FB电子邮件创建用户或查找现有用户,请将以下内容添加到您的用户模型:

  def self.find_for_facebook_mobile_client(fb_email)
    if user = User.where(:email => fb_email).first
      user
    else
      User.create!(:email => fb_email, :password => Devise.friendly_token[0,20])
    end
  end

I don't think fb_database_authenticatable is an accurate name, but I'll leave that as an exercise for the reader. 我不认为fb_database_authenticatable是一个准确的名字,但我会把它作为读者的练习。 Another exercise is caching/storing the FB access token, and perhaps avoiding the RT to FB with each call. 另一个练习是缓存/存储FB访问令牌,并且可能在每次调用时避免RT到FB。 You should note that the access token from the the mobile app and the rails app will be different if you do FB authentication on both sides, which I suspect most people will want to do. 您应该注意,如果您在双方都进行FB身份验证,那么来自移动应用程序和rails应用程序的访问令牌会有所不同,我怀疑大多数人都希望这样做。 This probably impacts your caching scheme. 这可能会影响您的缓存方案。

I think that does it - happy coding. 我认为这样做 - 快乐的编码。

If you are setting request headers in xcode 4.3.2 you could use: 如果您在xcode 4.3.2中设置请求标头,您可以使用:

[request setValue:@"12345" forHTTPHeaderField:@"fbaccesstoken"];

but you cannot use: 但你不能使用:

[request setValue:@"12345" forHTTPHeaderField:@"fb_access_token"]; // does NOT get set

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM