2011-08-29 123 views
7

我们有一个使用devise & omniauth允许通过Facebook身份验证登录的rails应用程序设置。我们还有一个移动应用程序,它目前使用http身份验证登录到Rails应用程序,方法是通过传递用户名&密码或传入http身份验证令牌。迄今为止,这一切都很有效。如何使用可选omniauth令牌作为身份验证令牌使用http身份验证

该移动应用程序还具有使用Facebook本身进行身份验证以及在其本身和Facebook之间直接接收用户Facebook令牌的能力。

我想填补这个空白,以便如果用户通过Facebook从移动应用程序登录并拥有他们的Facebook令牌,则允许将这个Facebook令牌用作Rails应用程序的身份验证,就好像他们已收到它从Facebook通过浏览器。

最终的结果将是,移动应用可以登录用户经由:

1)用户名/密码 或 2)HTTP认证令牌 或 3)omniauth(Facebook)的令牌

另外,在3)的情况下,如果用户还没有存在于Rails应用程序中,则需要创建用户 - 现在已经使用浏览器侧认证,因此可能没有什么可做的了。

我该如何在设计构造中最好地完成这件事?

回答

10

刚刚做到了这一点,但从未见过端到端的解决方案。

此地址点3. 1 & 2很容易实现设计和记录在别处。

不难将FB认证添加到您的网络应用程序中,说明在github上为omniauth和omniauth-facebook。

我相信如果你想这样做,以下是独立的,没有做omniauth-facebook集成。这与其他方法类似。我的想法是试图尽可能使用设计模型。

您将需要fb_graph宝石。

在移动客户端上,您可以使用FB进行相应的身份验证,并将返回的访问令牌放入您的http请求的头部。我使用头fb_access_token。 就像基本身份验证一样,您需要通过SSL发送该消息以避免嗅探令牌。使用头允许我在不更改请求的情况下交换基本身份验证和FB身份验证,但是如果您愿意,可以使用参数。

该解决方案实现了基于设计Authenticatable监管策略的监狱战略。这里的区别在于这个策略使用了一个名为fb_access_token的HTTP头,其中包含使用移动应用程序检索到的Facebook访问令牌字符串。

一旦你知道这一点,代码是非常简单的。

在文件中,在config/initializers目录中添加以下内容。我碰巧打电话给我的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) 

在配置/初始化,添加以下devise.rb:

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

为了让您无论是创建一个用户或发现基础上,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 

我不认为fb_database_authenticatable是一个准确的名字,但我会离开,作为一个练习留给读者。另一个运动是高速缓存/存储FB访问令牌,也许避免RT与每个呼叫到FB。你应该注意的是,如果你在两边,我相信大部分人会不愿意做FB验证从移动应用和Rails应用程序的访问令牌会有所不同。这可能会影响您的缓存方案。

我认为,做它 - 快乐编码。

+2

一些需要注意的:基本权限使用FB帐户不要提供电子邮件地址。你需要明确地询问用户这个权限。 – beeudoublez

+0

好一点@beeudoublez,这正是我在我的应用程序一样。 – Matt

0

如果您在Xcode 4.3.2 设置请求头,你可以使用:

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

,但你不能使用:

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