我能够通过一个丑陋的 hack 来解决这个问题。如果对这些模块的内部工作有更多了解的人会提出正确的方法来做到这一点,那就太好了——有很多方法我只是不知道足以提供一个干净的实现。我猴子修补了 oauth 助手以从签名请求中提取 oauth 令牌。我还没有完成实施,所以也许我会遇到更多麻烦。这也可以利用签名请求中包含的 user_id(facebook uid),而不是调用图形 api。
如果用户还没有按照此处记录的方式获得授权,我已对此进行了更新以包含重定向:
http://developers.facebook.com/docs/authentication/
“如果 JSON 对象不包含 user_id 参数,您应该按照正常的 OAuth 2.0 Web 应用程序流程将用户重定向到应用程序授权屏幕。”
但是,这会显示一个带有指向该 URL 链接的 facebook 徽标,然后将用户弹出 iframe - 我认为不应该存在的某种 iframe 被 facebook 失败。
module Devise
module Oauth
module InternalHelpers
def callback_action
access_token = nil
if params[:signed_request]
signed_data = FacebookSignedRequest.new(params[:signed_request]).valid?
if signed_data['oauth_token']
access_token = oauth_config.access_token_by_token(signed_data['oauth_token'])
else
return redirect_to oauth_config.authorize_url(:redirect_uri => oauth_redirect_uri)
end
elsif params[:code]
access_token = oauth_config.access_token_by_code(params[:code], oauth_redirect_uri)
else
return redirect_to oauth_config.authorize_url(:redirect_uri => oauth_redirect_uri)
end
self.resource = resource_class.send(oauth_model_callback, access_token, signed_in_resource) if access_token
if resource && resource.persisted? && resource.errors.empty?
set_oauth_flash_message :notice, :success
sign_in_and_redirect resource_name, resource, :event => :authentication
else
session[oauth_session_key] = access_token.token
clean_up_passwords(resource)
render_for_oauth
end
end
def valid_oauth_callback?
return !!params[:signed_request]
end
end
end
end
### For parsing the signed request
require 'base64'
require 'json'
require 'openssl'
# the code is copied from
# http://forum.developers.facebook.net/viewtopic.php?pid=250261
class FacebookSignedRequest
attr_reader :signed_request
def initialize(signed_request)
@signed_request = signed_request
end
def base64_url_decode str
encoded_str = str.gsub('-','+').gsub('_','/')
encoded_str += '=' while !(encoded_str.size % 4).zero?
Base64.decode64(encoded_str)
end
def valid?
secret = "facebook-secret"
#decode data
encoded_sig, payload = signed_request.split('.')
sig = base64_url_decode(encoded_sig).unpack("H*")[0]
data = JSON.parse base64_url_decode(payload)
if data['algorithm'].to_s.upcase != 'HMAC-SHA256'
# Rails.logger.error 'Unknown algorithm. Expected HMAC-SHA256'
return false
end
#check sig
expected_sig = OpenSSL::HMAC.hexdigest('sha256', secret, payload)
if expected_sig != sig
# Rails.logger.error 'Bad Signed JSON signature!'
return false
end
data
end
end