情况:
使用 Rails 3 和 OmniAuth,我有一个使用 Facebook 策略进行身份验证的应用程序。这个应用程序被构建为同样适用于 Web 和移动界面(ala Jquery-Mobile)。
挑战在于让 OmniAuth 向移动设备提供移动版 Facebook 登录屏幕,向桌面设备提供网络版。
我已经拼凑了一个解决方案,我将把它作为答案。
情况:
使用 Rails 3 和 OmniAuth,我有一个使用 Facebook 策略进行身份验证的应用程序。这个应用程序被构建为同样适用于 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-facebook,我必须设置显示选项,如下所示:
options[:authorize_params] = mobile_request? ? { :display => 'touch' } : { :display => 'page' }
您可以使用我找到的“弹出”、“触摸”或“页面”。
我尝试了第一个解决方案,但无法正常工作。经过大量搜索后,我发现 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 策略并添加 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