canvas app work, need to write tests for it
This commit is contained in:
parent
0937d2ba9d
commit
6a3f2f73d5
39
README
39
README
@ -36,7 +36,7 @@ shared login partial.
|
|||||||
<%= fb_connect_async_js %>
|
<%= fb_connect_async_js %>
|
||||||
<% if current_facebook_user %>
|
<% if current_facebook_user %>
|
||||||
<%= "Welcome #{current_facebook_user.first_name} #{current_facebook_user.last_name}!" %>
|
<%= "Welcome #{current_facebook_user.first_name} #{current_facebook_user.last_name}!" %>
|
||||||
or
|
or
|
||||||
<%= "Hello #{fb_name(current_facebook_user, :useyou => false)}!" # link to facebook profile
|
<%= "Hello #{fb_name(current_facebook_user, :useyou => false)}!" # link to facebook profile
|
||||||
%>
|
%>
|
||||||
<%= fb_logout_link("Logout of fb", request.url) %><br />
|
<%= fb_logout_link("Logout of fb", request.url) %><br />
|
||||||
@ -48,4 +48,41 @@ shared login partial.
|
|||||||
<%= fb_login_and_redirect('<your URL here>', :perms => 'email,user_birthday') %>
|
<%= fb_login_and_redirect('<your URL here>', :perms => 'email,user_birthday') %>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
|
Using with Canvas Applications
|
||||||
|
==============================
|
||||||
|
|
||||||
|
To improve integration with Facebook and iframe canvas applications, the primary goal
|
||||||
|
being things like FB.ui work as a dialog rather than a popup, the application
|
||||||
|
needs to be authenticated against the user's account via OAuth before use.
|
||||||
|
|
||||||
|
0. Prerequisite: You need a Facebook app. Have your canvas page name handy.
|
||||||
|
|
||||||
|
1. Install facebooker2.
|
||||||
|
|
||||||
|
2. Create config/facebooker.yml as above, but add the following key:
|
||||||
|
|
||||||
|
production:
|
||||||
|
canvas_page_name: <your canvas page name>
|
||||||
|
|
||||||
|
3. Add the following lines to your app/controllers/application_controller.rb
|
||||||
|
|
||||||
|
include Facebooker2::Rails::Controller::CanvasOAuth
|
||||||
|
|
||||||
|
ensure_canvas_connected_to_facebook :oauth_url, 'publish_stream'
|
||||||
|
create_facebook_oauth_callback :oauth
|
||||||
|
|
||||||
|
rescue_from Facebooker2::OAuthException do |exception|
|
||||||
|
redirect_to 'http://www.facebook.com/'
|
||||||
|
end
|
||||||
|
|
||||||
|
4. Create a route that generates a URL for the OAuth callback and calls the appropriate
|
||||||
|
action on your controller:
|
||||||
|
|
||||||
|
map.oauth '/oauth', :controller => :application, :action => :oauth
|
||||||
|
|
||||||
|
5. Your canvas application will now ensure that the current user has authorized
|
||||||
|
the application before anything else is allowed. The authorization and FB.ui dialogs
|
||||||
|
will appear inline instead of as popups, improving user experience.
|
||||||
|
|
||||||
Copyright (c) 2010 Mike Mangino, released under the MIT license
|
Copyright (c) 2010 Mike Mangino, released under the MIT license
|
||||||
|
Copyright (c) 2010 John Bintz, released under the MIT license
|
||||||
|
@ -1,35 +1,37 @@
|
|||||||
# Facebooker2
|
# Facebooker2
|
||||||
require "mogli"
|
require "mogli"
|
||||||
|
|
||||||
module Facebooker2
|
module Facebooker2
|
||||||
class NotConfigured < Exception; end
|
class NotConfigured < Exception; end
|
||||||
class << self
|
class << self
|
||||||
attr_accessor :api_key, :secret, :app_id
|
attr_accessor :api_key, :secret, :app_id, :canvas_page_name
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.secret
|
def self.secret
|
||||||
@secret || raise_unconfigured_exception
|
@secret || raise_unconfigured_exception
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.app_id
|
def self.app_id
|
||||||
@app_id || raise_unconfigured_exception
|
@app_id || raise_unconfigured_exception
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.raise_unconfigured_exception
|
def self.raise_unconfigured_exception
|
||||||
raise NotConfigured.new("No configuration provided for Facebooker2. Either set the app_id and secret or call Facebooker2.load_facebooker_yaml in an initializer")
|
raise NotConfigured.new("No configuration provided for Facebooker2. Either set the app_id and secret or call Facebooker2.load_facebooker_yaml in an initializer")
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.configuration=(hash)
|
def self.configuration=(hash)
|
||||||
self.api_key = hash[:api_key]
|
self.api_key = hash[:api_key]
|
||||||
self.secret = hash[:secret]
|
self.secret = hash[:secret]
|
||||||
self.app_id = hash[:app_id]
|
self.app_id = hash[:app_id]
|
||||||
|
self.canvas_page_name = hash[:canvas_page_name]
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.load_facebooker_yaml
|
def self.load_facebooker_yaml
|
||||||
config = YAML.load(File.read(File.join(::Rails.root,"config","facebooker.yml")))[::Rails.env]
|
config = YAML.load(File.read(File.join(::Rails.root,"config","facebooker.yml")))[::Rails.env]
|
||||||
raise NotConfigured.new("Unable to load configuration for #{::Rails.env} from facebooker.yml. Is it set up?") if config.nil?
|
raise NotConfigured.new("Unable to load configuration for #{::Rails.env} from facebooker.yml. Is it set up?") if config.nil?
|
||||||
self.configuration = config.with_indifferent_access
|
self.configuration = config.with_indifferent_access
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.cast_to_facebook_id(object)
|
def self.cast_to_facebook_id(object)
|
||||||
if object.kind_of?(Mogli::Profile)
|
if object.kind_of?(Mogli::Profile)
|
||||||
object.id
|
object.id
|
||||||
@ -43,8 +45,10 @@ end
|
|||||||
|
|
||||||
|
|
||||||
require "facebooker2/rails/controller"
|
require "facebooker2/rails/controller"
|
||||||
|
require "facebooker2/rails/controller/canvas_oauth"
|
||||||
require "facebooker2/rails/helpers/facebook_connect"
|
require "facebooker2/rails/helpers/facebook_connect"
|
||||||
require "facebooker2/rails/helpers/javascript"
|
require "facebooker2/rails/helpers/javascript"
|
||||||
require "facebooker2/rails/helpers/request_forms"
|
require "facebooker2/rails/helpers/request_forms"
|
||||||
require "facebooker2/rails/helpers/user"
|
require "facebooker2/rails/helpers/user"
|
||||||
require "facebooker2/rails/helpers"
|
require "facebooker2/rails/helpers"
|
||||||
|
require "facebooker2/oauth_exception"
|
||||||
|
4
lib/facebooker2/oauth_exception.rb
Normal file
4
lib/facebooker2/oauth_exception.rb
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
module Facebooker2
|
||||||
|
class OAuthException < StandardError; end
|
||||||
|
end
|
||||||
|
|
@ -3,31 +3,31 @@ require "hmac-sha2"
|
|||||||
module Facebooker2
|
module Facebooker2
|
||||||
module Rails
|
module Rails
|
||||||
module Controller
|
module Controller
|
||||||
|
|
||||||
def self.included(controller)
|
def self.included(controller)
|
||||||
controller.helper Facebooker2::Rails::Helpers
|
controller.helper Facebooker2::Rails::Helpers
|
||||||
controller.helper_method :current_facebook_user
|
controller.helper_method :current_facebook_user
|
||||||
controller.helper_method :current_facebook_client
|
controller.helper_method :current_facebook_client
|
||||||
controller.helper_method :facebook_params
|
controller.helper_method :facebook_params
|
||||||
end
|
end
|
||||||
|
|
||||||
def current_facebook_user
|
def current_facebook_user
|
||||||
fetch_client_and_user
|
fetch_client_and_user
|
||||||
@_current_facebook_user
|
@_current_facebook_user
|
||||||
end
|
end
|
||||||
|
|
||||||
def current_facebook_client
|
def current_facebook_client
|
||||||
fetch_client_and_user
|
fetch_client_and_user
|
||||||
@_current_facebook_client
|
@_current_facebook_client
|
||||||
end
|
end
|
||||||
|
|
||||||
def fetch_client_and_user
|
def fetch_client_and_user
|
||||||
return if @_fb_user_fetched
|
return if @_fb_user_fetched
|
||||||
fetch_client_and_user_from_cookie
|
fetch_client_and_user_from_cookie
|
||||||
fetch_client_and_user_from_signed_request unless @_current_facebook_client
|
fetch_client_and_user_from_signed_request unless @_current_facebook_client
|
||||||
@_fb_user_fetched = true
|
@_fb_user_fetched = true
|
||||||
end
|
end
|
||||||
|
|
||||||
def fetch_client_and_user_from_cookie
|
def fetch_client_and_user_from_cookie
|
||||||
app_id = Facebooker2.app_id
|
app_id = Facebooker2.app_id
|
||||||
if (hash_data = fb_cookie_hash_for_app_id(app_id)) and
|
if (hash_data = fb_cookie_hash_for_app_id(app_id)) and
|
||||||
@ -35,20 +35,20 @@ module Facebooker2
|
|||||||
fb_create_user_and_client(hash_data["access_token"],hash_data["expires"],hash_data["uid"])
|
fb_create_user_and_client(hash_data["access_token"],hash_data["expires"],hash_data["uid"])
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def fb_create_user_and_client(token,expires,userid)
|
def fb_create_user_and_client(token,expires,userid)
|
||||||
client = Mogli::Client.new(token,expires.to_i)
|
client = Mogli::Client.new(token,expires.to_i)
|
||||||
user = Mogli::User.new(:id=>userid)
|
user = Mogli::User.new(:id=>userid)
|
||||||
fb_sign_in_user_and_client(user,client)
|
fb_sign_in_user_and_client(user,client)
|
||||||
end
|
end
|
||||||
|
|
||||||
def fb_sign_in_user_and_client(user,client)
|
def fb_sign_in_user_and_client(user,client)
|
||||||
user.client = client
|
user.client = client
|
||||||
@_current_facebook_user = user
|
@_current_facebook_user = user
|
||||||
@_current_facebook_client = client
|
@_current_facebook_client = client
|
||||||
@_fb_user_fetched = true
|
@_fb_user_fetched = true
|
||||||
end
|
end
|
||||||
|
|
||||||
def fb_cookie_hash_for_app_id(app_id)
|
def fb_cookie_hash_for_app_id(app_id)
|
||||||
return nil unless fb_cookie_for_app_id?(app_id)
|
return nil unless fb_cookie_for_app_id?(app_id)
|
||||||
hash={}
|
hash={}
|
||||||
@ -59,15 +59,15 @@ module Facebooker2
|
|||||||
end
|
end
|
||||||
hash
|
hash
|
||||||
end
|
end
|
||||||
|
|
||||||
def fb_cookie_for_app_id?(app_id)
|
def fb_cookie_for_app_id?(app_id)
|
||||||
!fb_cookie_for_app_id(app_id).nil?
|
!fb_cookie_for_app_id(app_id).nil?
|
||||||
end
|
end
|
||||||
|
|
||||||
def fb_cookie_for_app_id(app_id)
|
def fb_cookie_for_app_id(app_id)
|
||||||
cookies["fbs_#{app_id}"]
|
cookies["fbs_#{app_id}"]
|
||||||
end
|
end
|
||||||
|
|
||||||
def fb_cookie_signature_correct?(hash,secret)
|
def fb_cookie_signature_correct?(hash,secret)
|
||||||
sorted_keys = hash.keys.reject {|k| k=="sig"}.sort
|
sorted_keys = hash.keys.reject {|k| k=="sig"}.sort
|
||||||
test_string = ""
|
test_string = ""
|
||||||
@ -77,13 +77,13 @@ module Facebooker2
|
|||||||
test_string += secret
|
test_string += secret
|
||||||
Digest::MD5.hexdigest(test_string) == hash["sig"]
|
Digest::MD5.hexdigest(test_string) == hash["sig"]
|
||||||
end
|
end
|
||||||
|
|
||||||
def fb_signed_request_json(encoded)
|
def fb_signed_request_json(encoded)
|
||||||
chars_to_add = 4-(encoded.size % 4)
|
chars_to_add = 4-(encoded.size % 4)
|
||||||
encoded += ("=" * chars_to_add)
|
encoded += ("=" * chars_to_add)
|
||||||
Base64.decode64(encoded)
|
Base64.decode64(encoded)
|
||||||
end
|
end
|
||||||
|
|
||||||
def facebook_params
|
def facebook_params
|
||||||
@facebook_param ||= fb_load_facebook_params
|
@facebook_param ||= fb_load_facebook_params
|
||||||
end
|
end
|
||||||
@ -93,20 +93,21 @@ module Facebooker2
|
|||||||
sig,encoded_json = params[:signed_request].split(".")
|
sig,encoded_json = params[:signed_request].split(".")
|
||||||
return {} unless fb_signed_request_sig_valid?(sig,encoded_json)
|
return {} unless fb_signed_request_sig_valid?(sig,encoded_json)
|
||||||
ActiveSupport::JSON.decode(fb_signed_request_json(encoded_json)).with_indifferent_access
|
ActiveSupport::JSON.decode(fb_signed_request_json(encoded_json)).with_indifferent_access
|
||||||
end
|
end
|
||||||
|
|
||||||
def fb_signed_request_sig_valid?(sig,encoded)
|
def fb_signed_request_sig_valid?(sig,encoded)
|
||||||
base64 = Base64.encode64(HMAC::SHA256.digest(Facebooker2.secret,encoded))
|
base64 = Base64.encode64(HMAC::SHA256.digest(Facebooker2.secret,encoded))
|
||||||
#now make the url changes that facebook makes
|
#now make the url changes that facebook makes
|
||||||
url_escaped_base64 = base64.gsub(/=*\n?$/,"").tr("+/","-_")
|
url_escaped_base64 = base64.gsub(/=*\n?$/,"").tr("+/","-_")
|
||||||
sig == url_escaped_base64
|
sig == url_escaped_base64
|
||||||
end
|
end
|
||||||
|
|
||||||
def fetch_client_and_user_from_signed_request
|
def fetch_client_and_user_from_signed_request
|
||||||
if facebook_params[:oauth_token]
|
if facebook_params[:oauth_token]
|
||||||
fb_create_user_and_client(facebook_params[:oauth_token],facebook_params[:expires],facebook_params[:user_id])
|
fb_create_user_and_client(facebook_params[:oauth_token],facebook_params[:expires],facebook_params[:user_id])
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
55
lib/facebooker2/rails/controller/canvas_oauth.rb
Normal file
55
lib/facebooker2/rails/controller/canvas_oauth.rb
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
module Facebooker2
|
||||||
|
module Rails
|
||||||
|
module Controller
|
||||||
|
module CanvasOAuth
|
||||||
|
def self.included(controller)
|
||||||
|
controller.extend(CanvasOAuthClass)
|
||||||
|
|
||||||
|
class << controller
|
||||||
|
attr_accessor :_facebooker_oauth_callback_url, :_facebooker_scope
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
protected
|
||||||
|
def canvas_oauth_connect
|
||||||
|
raise "Canvas page name not defined! Define it in config/facebooker.yml as #{::Rails.env}: canvas_page_name: <your url>." if !Facebooker2.canvas_page_name
|
||||||
|
if params[:error]
|
||||||
|
raise Facebooker2::OAuthException.new(params[:error][:message])
|
||||||
|
else
|
||||||
|
redirect_to ('http://apps.facebook.com/' + Facebooker2.canvas_page_name) if params[:code]
|
||||||
|
end
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
def ensure_canvas_connected
|
||||||
|
case self.class._facebooker_oauth_callback_url
|
||||||
|
when Symbol
|
||||||
|
callback_url = send(self.class._facebooker_oauth_callback_url)
|
||||||
|
end
|
||||||
|
|
||||||
|
if current_facebook_user == nil && !params[:code] && !params[:error]
|
||||||
|
render :text => "<script>top.location.href = 'https://graph.facebook.com/oauth/authorize?client_id=#{Facebooker2.app_id}&redirect_uri=#{callback_url}&scope=#{[ self.class._facebooker_scope ].flatten * ','}'</script>"
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
module CanvasOAuthClass
|
||||||
|
def ensure_canvas_connected_to_facebook(oauth_callback_url, *scope)
|
||||||
|
self._facebooker_oauth_callback_url = oauth_callback_url
|
||||||
|
self._facebooker_scope = scope
|
||||||
|
|
||||||
|
before_filter :ensure_canvas_connected
|
||||||
|
end
|
||||||
|
|
||||||
|
def create_facebook_oauth_callback(method_name)
|
||||||
|
self.class_eval(<<-EOT)
|
||||||
|
def #{method_name}
|
||||||
|
return canvas_oauth_connect
|
||||||
|
end
|
||||||
|
EOT
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
0
spec/rails/controller/canvas_oauth_spec.rb
Normal file
0
spec/rails/controller/canvas_oauth_spec.rb
Normal file
Loading…
Reference in New Issue
Block a user