Permalink
Browse files

Update FGraph rails helper to support OAuth 2.0

Allow FGraph.oauth_access_token to pass empty redirect_uri
  • Loading branch information...
1 parent 7fd1045 commit 3e79fbb42221b5aec587770ab2bdee57b851ebe7 @jugend committed Dec 27, 2011
Showing with 109 additions and 93 deletions.
  1. +4 −0 History
  2. +2 −2 VERSION.yml
  3. +2 −2 fgraph.gemspec
  4. +4 −3 lib/fgraph.rb
  5. +89 −79 lib/fgraph/rails/fgraph_helper.rb
  6. +7 −6 lib/fgraph/rails/fgraph_tag_helper.rb
  7. +1 −1 test/fgraph_test.rb
View
@@ -1,3 +1,7 @@
+v0.7.0
+ * Set FGraph.oauth_access_token to pass empty redirect_uri param by default
+ * Update FGraph Rails helper to support FB OAuth 2.0
+
v0.6.2
* Change RAILS_ROOT to Rails.root to avoid deprecated message in Rails 3
View
@@ -1,5 +1,5 @@
---
:major: 0
:build:
-:minor: 6
-:patch: 2
+:minor: 7
+:patch: 0
View
@@ -5,11 +5,11 @@
Gem::Specification.new do |s|
s.name = "fgraph"
- s.version = "0.6.2"
+ s.version = "0.7.0"
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
s.authors = ["Herryanto Siatono"]
- s.date = "2011-11-19"
+ s.date = "2011-12-27"
s.description = "Ruby Facebook Graph API"
s.email = "herryanto@gmail.com"
s.extra_rdoc_files = [
View
@@ -191,7 +191,7 @@ def oauth_authorize_url(client_id, redirect_uri, options={})
# # redirect_uri=http://www.example.com/oauth_redirect&
# # code=...
# FGraph.oauth_access_token('[client id]', '[client secret]',
- # :redirect_uri => ''http://www.example.com/oauth_redirect',
+ # :redirect_uri => 'http://www.example.com/oauth_redirect',
# :code => '[authorization code]')
#
# Application access token requires <tt>:type => 'client_cred'</td> option. Used to access application
@@ -206,7 +206,8 @@ def oauth_authorize_url(client_id, redirect_uri, options={})
def oauth_access_token(client_id, client_secret, options={})
url = self.format_url('/oauth/access_token', {
:client_id => client_id,
- :client_secret => client_secret
+ :client_secret => client_secret,
+ :redirect_uri => ''
}.merge(options || {}))
response = self.perform_get(url)
@@ -353,7 +354,7 @@ def format_url(path, options={})
options = stringified_options
options.each do |option|
- next if option[1].blank?
+ next unless option[0]
url << "&" if option_count > 0
url << "#{option[0]}=#{CGI.escape(option[1].to_s)}"
option_count += 1
@@ -1,88 +1,98 @@
module FGraph
module Rails
- module FGraphHelper
-
- # Access FGraph.config initialized with values set in <tt>[RAILS_ROOT]/config/fgraph.yml</tt>.
- def fgraph_config
- FGraph.config || {}
- end
+ module FGraphHelper
+
+ # Access FGraph.config initialized with values set in <tt>[RAILS_ROOT]/config/fgraph.yml</tt>.
+ def fgraph_config
+ FGraph.config || {}
+ end
+
+ # Return Facebook session, default to retrieve session from cookies.
+ def fgraph_session(app_id = fgraph_config['app_id'],
+ app_secret = fgraph_config['app_secret'])
+
+ return @fgraph_session if @fgraph_session
+ @fgraph_session = fgraph_session_cookies(app_id, app_secret)
+ end
+
+ # Return Facebook session cookies.
+ def fgraph_session_cookies(app_id = fgraph_config['app_id'],
+ app_secret = fgraph_config['app_secret'])
- # Return Facebook session, default to retrieve session from cookies.
- def fgraph_session(app_id = fgraph_config['app_id'],
- app_secret = fgraph_config['app_secret'])
-
- return @fgraph_session if @fgraph_session
- @fgraph_session = fgraph_session_cookies(app_id, app_secret)
- end
-
- # Return Facebook session cookies.
- def fgraph_session_cookies(app_id = fgraph_config['app_id'],
- app_secret = fgraph_config['app_secret'])
-
- return @fgraph_session_cookies if @fgraph_session_cookies
- return if @fgraph_session_cookies == false
-
- # retrieve session from cookies
- fbs_cookies = request.cookies["fbs_#{app_id}"]
- if app_id.blank? or app_secret.blank? or fbs_cookies.blank?
- return @fgraph_session_cookies = false
- end
+ return @fgraph_session_cookies if @fgraph_session_cookies
+ return if @fgraph_session_cookies == false
- # Parse facebook cookies
- fbs_cookies = CGI.parse(fbs_cookies.gsub(/(^\"|\"$)/, ''))
- session_cookies = {}
- fbs_cookies.each do |key, value|
- session_cookies[key] = value[0]
- end
-
- # Validate session cookies
- cookie_message = ''
- session_cookies_list = session_cookies.sort
- session_cookies_list.each do |cookie|
- cookie_message += "#{cookie[0]}=#{cookie[1]}" if cookie[0] != 'sig'
- end
+ fbsr_cookie = request.cookies["fbsr_#{app_id}"]
+ if app_id.blank? or app_secret.blank? or fbsr_cookie.blank?
+ return @fgraph_session_cookies = false
+ end
- # Message digest does not match
- if Digest::MD5.hexdigest(cookie_message + app_secret) != session_cookies['sig']
- @fgraph_session_cookies = false
- end
+ # Get authorization code and access token
+ signed_request = fgraph_parse_signed_request(fbsr_cookie, app_secret)
+ resp = FGraph.oauth_access_token(app_id, app_secret, :code => signed_request['code'])
+
+ @fgraph_session_cookies = {
+ 'access_token' => resp['access_token']
+ }
+ end
+
+ def fgraph_base64_url_decode(str)
+ str += '=' * (4 - str.length.modulo(4))
+ Base64.decode64(str.tr('-_', '+/'))
+ end
+
+ # Parses a signed request string provided by Facebook to canvas apps or in a secure cookie.
+ #
+ # @param Input the signed request from Facebook
+ # @raise RuntimeError if the signature is incomplete, invalid, or using an unsupported algorithm
+ # @return A hash of the validated request information
+ def fgraph_parse_signed_request(input, app_secret)
+ encoded_sig, encoded_envelope = input.split('.', 2)
+ raise FGraph::OAuthError, 'SignedRequest: Invalid (incomplete) signature data' unless encoded_sig && encoded_envelope
- @fgraph_session_cookies = session_cookies
- end
-
- def fgraph_access_token
- return unless fgraph_session
- fgraph_session['access_token']
- end
-
- def fgraph_logged_in?
- return true if fgraph_session and fgraph_access_token
- end
-
- # Currently logged in facebook user
- def fgraph_current_user
- return @fgraph_current_user if @fgraph_current_user
- @fgraph_current_user = fgraph_client.me
- end
-
- # Alias for fgraph_current_user
- def fgraph_user
- fgraph_current_user
- end
-
- # Return FGraph::Client instance initialized with settings set in <tt>fgraph.yml</tt>.
- # Initialized with <tt>:access_token</tt> as well if Facebook session exists.
- def fgraph_client
- return @fgraph_client if @fgraph_client
-
- @fgraph_client = FGraph::Client.new(
- :client_id => fgraph_config['app_id'],
- :client_secret => fgraph_config['app_secret'],
- :access_token => fgraph_access_token
- )
- end
-
- # Return Facebook object picture url: http://graph.facebook.com/[id]/picture
+ signature = fgraph_base64_url_decode(encoded_sig).unpack("H*").first
+ envelope = ActiveSupport::JSON.decode(fgraph_base64_url_decode(encoded_envelope))
+ raise FGraph::OAuthError, "SignedRequest: Unsupported algorithm #{envelope['algorithm']}" if envelope['algorithm'] != 'HMAC-SHA256'
+
+ hmac = OpenSSL::HMAC.hexdigest(OpenSSL::Digest::SHA256.new, app_secret, encoded_envelope)
+ raise FGraph::OAuthError, 'SignedRequest: Invalid signature' if (signature != hmac)
+
+ envelope
+ end
+
+ def fgraph_access_token
+ return unless fgraph_session
+ fgraph_session['access_token']
+ end
+
+ def fgraph_logged_in?
+ return true if fgraph_session and fgraph_access_token
+ end
+
+ # Currently logged in facebook user
+ def fgraph_current_user
+ return @fgraph_current_user if @fgraph_current_user
+ @fgraph_current_user = fgraph_client.me
+ end
+
+ # Alias for fgraph_current_user
+ def fgraph_user
+ fgraph_current_user
+ end
+
+ # Return FGraph::Client instance initialized with settings set in <tt>fgraph.yml</tt>.
+ # Initialized with <tt>:access_token</tt> as well if Facebook session exists.
+ def fgraph_client
+ return @fgraph_client if @fgraph_client
+
+ @fgraph_client = FGraph::Client.new(
+ :client_id => fgraph_config['app_id'],
+ :client_secret => fgraph_config['app_secret'],
+ :access_token => fgraph_access_token
+ )
+ end
+
+ # Return Facebook object picture url: http://graph.facebook.com/[id]/picture
#
# ==== Type Options
# * <tt>square</tt> - 50x50 (default)
@@ -40,12 +40,13 @@ def fgraph_javascript_init_tag(options={})
window.afterFbAsyncInit();
}
};
- (function() {
- var e = document.createElement('script'); e.async = true;
- e.src = document.location.protocol +
- '//connect.facebook.net/en_US/all.js';
- document.getElementById('fb-root').appendChild(e);
- }());
+
+ (function(d) {
+ var js, id = 'facebook-jssdk'; if (d.getElementById(id)) {return;}
+ js = d.createElement('script'); js.id = id; js.async = true;
+ js.src = "//connect.facebook.net/en_US/all.js";
+ d.getElementsByTagName('head')[0].appendChild(js);
+ }(document));
</script>
}
else
View
@@ -229,7 +229,7 @@ class FGraphTest < Test::Unit::TestCase
should "return URL without empty options" do
formatted_url = FGraph.format_url('/test', {:username => 'john', :age => nil})
- assert_equal "https://graph.facebook.com/test?username=john", formatted_url
+ assert_equal "https://graph.facebook.com/test?username=john&age=", formatted_url
end
end

0 comments on commit 3e79fbb

Please sign in to comment.