canvas app work, need to write tests for it

This commit is contained in:
John Bintz 2010-09-17 13:12:58 -04:00
parent 0937d2ba9d
commit 6a3f2f73d5
6 changed files with 129 additions and 28 deletions

39
README
View File

@ -36,7 +36,7 @@ shared login partial.
<%= fb_connect_async_js %>
<% if current_facebook_user %>
<%= "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
%>
<%= 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') %>
<% 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 John Bintz, released under the MIT license

View File

@ -1,35 +1,37 @@
# Facebooker2
require "mogli"
module Facebooker2
class NotConfigured < Exception; end
class << self
attr_accessor :api_key, :secret, :app_id
attr_accessor :api_key, :secret, :app_id, :canvas_page_name
end
def self.secret
@secret || raise_unconfigured_exception
@secret || raise_unconfigured_exception
end
def self.app_id
@app_id || raise_unconfigured_exception
end
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")
end
def self.configuration=(hash)
self.api_key = hash[:api_key]
self.secret = hash[:secret]
self.app_id = hash[:app_id]
self.canvas_page_name = hash[:canvas_page_name]
end
def self.load_facebooker_yaml
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?
self.configuration = config.with_indifferent_access
end
def self.cast_to_facebook_id(object)
if object.kind_of?(Mogli::Profile)
object.id
@ -43,8 +45,10 @@ end
require "facebooker2/rails/controller"
require "facebooker2/rails/controller/canvas_oauth"
require "facebooker2/rails/helpers/facebook_connect"
require "facebooker2/rails/helpers/javascript"
require "facebooker2/rails/helpers/request_forms"
require "facebooker2/rails/helpers/user"
require "facebooker2/rails/helpers"
require "facebooker2/rails/helpers"
require "facebooker2/oauth_exception"

View File

@ -0,0 +1,4 @@
module Facebooker2
class OAuthException < StandardError; end
end

View File

@ -3,31 +3,31 @@ require "hmac-sha2"
module Facebooker2
module Rails
module Controller
def self.included(controller)
controller.helper Facebooker2::Rails::Helpers
controller.helper_method :current_facebook_user
controller.helper_method :current_facebook_client
controller.helper_method :facebook_params
end
def current_facebook_user
fetch_client_and_user
@_current_facebook_user
end
def current_facebook_client
fetch_client_and_user
@_current_facebook_client
end
def fetch_client_and_user
return if @_fb_user_fetched
fetch_client_and_user_from_cookie
fetch_client_and_user_from_signed_request unless @_current_facebook_client
@_fb_user_fetched = true
end
def fetch_client_and_user_from_cookie
app_id = Facebooker2.app_id
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"])
end
end
def fb_create_user_and_client(token,expires,userid)
client = Mogli::Client.new(token,expires.to_i)
user = Mogli::User.new(:id=>userid)
fb_sign_in_user_and_client(user,client)
fb_sign_in_user_and_client(user,client)
end
def fb_sign_in_user_and_client(user,client)
user.client = client
@_current_facebook_user = user
@_current_facebook_client = client
@_fb_user_fetched = true
end
def fb_cookie_hash_for_app_id(app_id)
return nil unless fb_cookie_for_app_id?(app_id)
hash={}
@ -59,15 +59,15 @@ module Facebooker2
end
hash
end
def fb_cookie_for_app_id?(app_id)
!fb_cookie_for_app_id(app_id).nil?
end
def fb_cookie_for_app_id(app_id)
cookies["fbs_#{app_id}"]
end
def fb_cookie_signature_correct?(hash,secret)
sorted_keys = hash.keys.reject {|k| k=="sig"}.sort
test_string = ""
@ -77,13 +77,13 @@ module Facebooker2
test_string += secret
Digest::MD5.hexdigest(test_string) == hash["sig"]
end
def fb_signed_request_json(encoded)
chars_to_add = 4-(encoded.size % 4)
encoded += ("=" * chars_to_add)
Base64.decode64(encoded)
end
def facebook_params
@facebook_param ||= fb_load_facebook_params
end
@ -93,20 +93,21 @@ module Facebooker2
sig,encoded_json = params[:signed_request].split(".")
return {} unless fb_signed_request_sig_valid?(sig,encoded_json)
ActiveSupport::JSON.decode(fb_signed_request_json(encoded_json)).with_indifferent_access
end
def fb_signed_request_sig_valid?(sig,encoded)
end
def fb_signed_request_sig_valid?(sig,encoded)
base64 = Base64.encode64(HMAC::SHA256.digest(Facebooker2.secret,encoded))
#now make the url changes that facebook makes
url_escaped_base64 = base64.gsub(/=*\n?$/,"").tr("+/","-_")
sig == url_escaped_base64
end
def fetch_client_and_user_from_signed_request
if facebook_params[:oauth_token]
fb_create_user_and_client(facebook_params[:oauth_token],facebook_params[:expires],facebook_params[:user_id])
end
end
end
end
end

View 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