使用Omniauth和Rails 3在Facebook登录时在Web和触摸界面之间切换

情况:

使用Rails 3和OmniAuth,我有一个使用Facebook策略进行身份validation的应用程序。 这个应用程序的构建同样适用于Web和移动界面(ala Jquery-Mobile)。

面临的挑战是让OmniAuth向移动设备提供Facebook登录屏幕的移动版本,为桌面设备提供网络版本。

我已经破解了一个解决方案,我将其作为答案。

实际上,由于OmniAuth :: Strategies已经是Rack中间件,它甚至更简单。 只需覆盖request_phase方法并检查策略中存在的移动user_agent的@env实例变量:

module OmniAuth module Strategies class Facebook < OAuth2 MOBILE_USER_AGENTS = 'webos|ipod|iphone|mobile' def request_phase options[:scope] ||= "email,offline_access" options[:display] = mobile_request? ? 'touch' : 'page' super end def mobile_request? ua = Rack::Request.new(@env).user_agent.to_s ua.downcase =~ Regexp.new(MOBILE_USER_AGENTS) end end end end 

对于现代设计/ omniauth(> = 1.0),请在config / initializers / devise.rb中使用:

 FACEBOOK_SETUP_PROC = lambda do |env| request = Rack::Request.new(env) mobile_device = request.user_agent =~ /Mobile|webOS/i request.env['omniauth.strategy'].options[:display] = mobile_device ? "touch" : "page" end config.omniauth :facebook, FACEBOOK_APP_ID, FACEBOOK_APP_SECRET, :scope => 'email,offline_access', :setup => FACEBOOK_SETUP_PROC, :client_options => { :ssl => { :ca_file => Rails.root.join("config/ca-bundle.crt").to_s }} 

我尝试了第一个解决方案,但我无法让它工作。 经过多次搜索,我发现Omniauth有一个选项“:setup => true”,允许动态设置参数,例如:Facebook OAuth所需的:display选项。

首先打开:setup选项。

 config.omniauth :facebook, APP_CONFIG["fb_app_id"], APP_CONFIG["fb_app_secret"], {:scope => 'email, offline_access', :setup => true} 

然后添加第二条路线(设置路线):

  devise_for :users, :controllers => { :omniauth_callbacks => "users/omniauth_callbacks" } do get '/users/auth/:provider' => 'users/omniauth_callbacks#passthru' get '/users/auth/:provider/setup' => 'users/omniauth_callbacks#setup' end 

添加此控制器。 如果您遵循设计手册,您可能已经拥有它。

 class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController def setup request.env['omniauth.strategy'].options[:display] = mobile_device? ? "touch" : "page" render :text => "Setup complete.", :status => 404 end end 

将此方法添加到ApplicationController中:

 def mobile_device? if session[:mobile_param] session[:mobile_param] == "1" else request.user_agent =~ /Mobile|webOS/ end end 

完成!

批准的答案有效,除了我不得不换一行。 对于当前版本的omniauth-facebook,我必须设置display选项,如下所示:

 options[:authorize_params] = mobile_request? ? { :display => 'touch' } : { :display => 'page' } 

你可以使用我发现的’popup’,’touch’或’page’。

我的解决方案非常复杂,需要修改OmniAuth Facebook策略并添加Rack中间件。

首先,我添加了一个类属性并更改了OmniAuth :: Strategies :: Facebook中的方法(我把它放在我的omniauth.rb配置文件的末尾,但它属于lib目录):

 module OmniAuth module Strategies class Facebook < OAuth2 cattr_accessor :display # new def request_phase options[:scope] ||= "email,offline_access" options[:display] = OmniAuth::Strategies::Facebook.display || nil # new super end end end end 

其次,我添加了一个Rack中间件,以确定请求是否来自移动设备,然后相应地设置显示:

 module Rack class FacebookMobileOmniauth def initialize(app) @app = app end MOBILE_USER_AGENTS = 'palm|blackberry|nokia|phone|midp|mobi|symbian|chtml|ericsson|minimo|' + 'audiovox|motorola|samsung|telit|upg1|windows ce|ucweb|astel|plucker|' + 'x320|x240|j2me|sgh|portable|sprint|docomo|kddi|softbank|android|mmp|' + 'pdxgw|netfront|xiino|vodafone|portalmmm|sagem|mot-|sie-|ipod|up\\.b|' + 'webos|amoi|novarra|cdm|alcatel|pocket|ipad|iphone|mobileexplorer|' + 'mobile' def call(env) request = Request.new(env) if request.user_agent.to_s.downcase =~ Regexp.new(MOBILE_USER_AGENTS) OmniAuth::Strategies::Facebook.display = 'touch' else OmniAuth::Strategies::Facebook.display = nil end return @app.call(env) end end end 

最后,我将Rack中间件添加到我的config.ru:

  require ::File.expand_path('../config/environment', __FILE__) use Rack::FacebookMobileOmniauth # new run Mystupid::Application