rfacebook 0.5.1 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
data/README CHANGED
@@ -1,3 +1,78 @@
1
+ ============================== FACEBOOK RAILS CONTROLLER EXTENSIONS NOTES ==============================
2
+
3
+ Rails Applications:
4
+
5
+ First, set up your Facebook Developer account properly. By properly,
6
+ I mean that you need to set your callback URL to be the root of your
7
+ Rails application. For example:
8
+
9
+ Callback URL >>> http://www.mycrazywebsite.com/ ***NOTE: trailing slash is necessary
10
+ Canvas Page URL >>> http://apps.facebook.com/mycrazywebsite
11
+
12
+ Then, whenever a user visits
13
+
14
+ http://apps.facebook.com/mycrazywebsite/someRailsController/someAction
15
+
16
+ ...the Facebook API will call:
17
+
18
+ http://www.mycrazywebsite.com/someRailsController/someAction
19
+
20
+
21
+ You can get this behavior by installing the new RFacebook gem. After
22
+ installing the gem, make your ApplicationController look like this:
23
+
24
+ >>>>>>>>>>>> require "facebook_rails_controller_extensions"
25
+ >>>>>>>>>>>>
26
+ >>>>>>>>>>>> class ApplicationController < ActionController::Base
27
+ >>>>>>>>>>>>
28
+ >>>>>>>>>>>> # additions to integrate Facebook into controllers
29
+ >>>>>>>>>>>> include RFacebook::RailsControllerExtensions
30
+ >>>>>>>>>>>>
31
+ >>>>>>>>>>>> def facebook_api_key
32
+ >>>>>>>>>>>> return "<YOUR API KEY HERE>"
33
+ >>>>>>>>>>>> end
34
+ >>>>>>>>>>>>
35
+ >>>>>>>>>>>> def facebook_api_secret
36
+ >>>>>>>>>>>> return "<YOUR API SECRET HERE>"
37
+ >>>>>>>>>>>> end
38
+ >>>>>>>>>>>>
39
+ >>>>>>>>>>>> end
40
+
41
+ You will have access to 2 new items in your controllers:
42
+
43
+ (1) "fbsession"
44
+ Contains a FacebookWebSession. Will return false for "is_valid?" if you haven't forced the user to log in.
45
+
46
+
47
+ (2) "fbparams"
48
+ Contains all the params that are prefixed with "fb_sig_". You shouldn't ever *really* need this, but its there just in case.
49
+
50
+
51
+ And, you have a few new filters to try out:
52
+
53
+ (1) require_facebook_login
54
+ (2) require_facebook_install
55
+
56
+ For example, one of my Rails controllers looks like this:
57
+
58
+ >>>>>>>>>>>> class FacebookController < ApplicationController
59
+ >>>>>>>>>>>>
60
+ >>>>>>>>>>>> before_filter :require_facebook_install # require users to install the application (less intrusive is to require_facebook_login)
61
+ >>>>>>>>>>>>
62
+ >>>>>>>>>>>> def index
63
+ >>>>>>>>>>>> xml = fbsession.users_getInfo(:uids => [fbsession.session_user_id], :fields => ["first_name", "last_name"])
64
+ >>>>>>>>>>>> @firstName = xml.at("first_name").inner_html
65
+ >>>>>>>>>>>> @lastName = xml.at("last_name").inner_html
66
+ >>>>>>>>>>>> end
67
+ >>>>>>>>>>>>
68
+ >>>>>>>>>>>>
69
+ >>>>>>>>>>>> end
70
+
71
+
72
+
73
+ ============================== FACEBOOK PLATFORM NOTES ==============================
74
+
75
+
1
76
  ============================== IMPORTANT NOTES ==============================
2
77
 
3
78
 
@@ -64,7 +64,7 @@ class FacebookDesktopSession < FacebookSession
64
64
  optionalHideCheckbox = (hidecheckbox == true) ? "&hide_checkbox=true" : ""
65
65
 
66
66
  # build and return URL
67
- return "http://#{LOGIN_SERVER_BASE_URL}#{LOGIN_SERVER_PATH}?v=1.0&api_key=#{@api_key}&auth_token=#{@desktop_auth_token}#{optionalPopup}#{optionalNext}#{optionalSkipCookie}#{optionalHideCheckbox}"
67
+ return "http://#{WWW_SERVER_BASE_URL}#{WWW_PATH_LOGIN}?v=1.0&api_key=#{@api_key}&auth_token=#{@desktop_auth_token}#{optionalPopup}#{optionalNext}#{optionalSkipCookie}#{optionalHideCheckbox}"
68
68
  end
69
69
 
70
70
 
@@ -97,13 +97,21 @@ class FacebookDesktopSession < FacebookSession
97
97
 
98
98
  # determine the current user's id
99
99
  result = call_method("users.getLoggedInUser")
100
- @session_uid = result.at("users_getLoggedInUser_response").inner_html
100
+ @session_user_id = result.at("users_getLoggedInUser_response").inner_html
101
101
  end
102
102
 
103
103
  def is_valid?
104
104
  return (is_activated? and !session_expired?)
105
105
  end
106
106
 
107
+ def session_user_id
108
+ return @session_user_id
109
+ end
110
+
111
+ def session_key
112
+ return @session_key
113
+ end
114
+
107
115
  protected
108
116
 
109
117
  def is_activated?
@@ -118,7 +126,7 @@ class FacebookDesktopSession < FacebookSession
118
126
  def activate_with_token(auth_token)
119
127
  result = call_method("auth.getSession", {:auth_token => auth_token}, true)
120
128
  if result != nil
121
- @session_uid = result.at("uid").inner_html
129
+ @session_user_id = result.at("uid").inner_html
122
130
  @session_key = result.at("session_key").inner_html
123
131
  @session_secret = result.at("secret").inner_html
124
132
  end
@@ -0,0 +1,120 @@
1
+ require "facebook_web_session"
2
+
3
+ module RFacebook
4
+
5
+ module RailsControllerExtensions
6
+
7
+
8
+ # SECTION: Exceptions
9
+
10
+ class APIKeyNeededException < Exception; end
11
+ class APISecretNeededException < Exception; end
12
+
13
+ # SECTION: Template Methods (must be implemented by concrete subclass)
14
+
15
+ def facebook_api_key
16
+ raise APIKeyNeededException
17
+ end
18
+
19
+ def facebook_api_secret
20
+ raise APISecretNeededException
21
+ end
22
+
23
+
24
+
25
+ # SECTION: Required Methods
26
+
27
+ def fbparams
28
+
29
+ @fbparams ||= {};
30
+
31
+ # try to get fbparams from the params hash
32
+ if (@fbparams.length <= 0)
33
+ @fbparams = fbsession.get_fb_sig_params(params)
34
+ end
35
+
36
+ # else, try to get fbparams from the cookies hash
37
+ # TODO: we don't write anything into the cookie, so this is kind of pointless right now
38
+ if (@fbparams.length <= 0)
39
+ @fbparams = fbsession.get_fb_sig_params(cookies)
40
+ end
41
+
42
+ return @fbparams
43
+
44
+ end
45
+
46
+ def fbsession
47
+
48
+ if !@fbsession
49
+
50
+ # create a Facebook session that can be used by the controller
51
+ @fbsession = FacebookWebSession.new(facebook_api_key, facebook_api_secret)
52
+
53
+ # now we need to activate the session somehow. If the signature parameters are bad, then we don't make the session
54
+ if fbparams
55
+ # these might be nil
56
+ facebookUid = fbparams["user"]
57
+ facebookSessionKey = fbparams["session_key"]
58
+ expirationTime = fbparams["expires"]
59
+
60
+ # Method 1: user logged in and was redirected to our site (iframe/external)
61
+ if ( params["auth_token"] )
62
+ @fbsession.activate_with_token(params["auth_token"])
63
+ # Method 2: we have the user id and key from the fb_sig_ params
64
+ elsif (facebookUid and facebookSessionKey and expirationTime)
65
+ @fbsession.activate_with_previous_session(facebookSessionKey, facebookUid, expirationTime)
66
+ end
67
+ end
68
+
69
+ end
70
+
71
+ return @fbsession
72
+
73
+ end
74
+
75
+ # SECTION: Helpful Methods
76
+
77
+ def facebook_redirect_to(url)
78
+
79
+ if (in_facebook_canvas? and !in_facebook_iframe?)
80
+ render :text => "<fb:redirect url=\"#{url}\" />"
81
+
82
+ elsif url =~ /^https?:\/\/([^\/]*\.)?facebook\.com(:\d+)?/i # TODO: why doesn't this just check for iframe?
83
+ render :text => "<script type=\"text/javascript\">\ntop.location.href = \"#{url}\";\n</script>"
84
+
85
+ else
86
+ redirect_to url
87
+
88
+ end
89
+
90
+ end
91
+
92
+ def in_facebook_canvas?
93
+ return (fbparams["in_fbframe"] != nil)
94
+ end
95
+
96
+ def in_facebook_iframe?
97
+ return (fbparams["in_iframe"] != nil)
98
+ end
99
+
100
+ def require_facebook_login
101
+ sess = fbsession
102
+ if (sess and !sess.is_valid?)
103
+ if in_facebook_canvas?
104
+ render :text => "<fb:redirect url=\"#{sess.get_login_url(:canvas=>true)}\" />"
105
+ else
106
+ redirect_to sess.get_login_url
107
+ end
108
+ end
109
+ end
110
+
111
+ def require_facebook_install
112
+ sess = fbsession
113
+ if (sess and !sess.is_valid? and in_facebook_canvas?)
114
+ render :text => "<fb:redirect url=\"#{sess.get_install_url(:canvas=>true)}\" />"
115
+ end
116
+ end
117
+
118
+ end
119
+
120
+ end
@@ -39,14 +39,17 @@ require "hpricot"
39
39
 
40
40
  module RFacebook
41
41
 
42
- API_SERVER_BASE_URL = "api.facebook.com"
43
- LOGIN_SERVER_BASE_URL = "www.facebook.com"
44
- API_SERVER_PATH = "/restserver.php"
45
- LOGIN_SERVER_PATH = "/login.php"
42
+ API_SERVER_BASE_URL = "api.f8.facebook.com"
43
+ API_PATH_REST = "/restserver.php"
44
+
45
+ WWW_SERVER_BASE_URL = "www.f8.facebook.com"
46
+ WWW_PATH_LOGIN = "/login.php"
47
+ WWW_PATH_ADD = "/add.php"
48
+ WWW_PATH_INSTALL = "/install.php"
46
49
 
47
50
  class FacebookSession
48
51
 
49
- attr_reader :session_uid, :session_key
52
+ # error handling accessors
50
53
  attr_reader :last_call_was_successful, :last_error
51
54
  attr_writer :suppress_exceptions
52
55
 
@@ -73,7 +76,7 @@ class FacebookSession
73
76
 
74
77
  # calculated parameters
75
78
  @api_server_base_url = API_SERVER_BASE_URL
76
- @api_server_path = API_SERVER_PATH
79
+ @api_server_path = API_PATH_REST
77
80
 
78
81
  # optional parameters
79
82
  @suppress_exceptions = suppress_exceptions
@@ -83,37 +86,42 @@ class FacebookSession
83
86
  @last_error = nil
84
87
  @session_expired = false
85
88
 
86
- # virtual members (subclasses will set these)
87
- @session_uid = nil
88
- @session_key = nil
89
-
90
89
  end
91
90
 
92
91
  def session_expired?
93
92
  return (@session_expired == true)
94
93
  end
95
-
96
- protected
97
-
98
- # SECTION: Protected Abstract Interface
94
+
95
+ # SECTION: Public Abstract Interface
96
+
97
+ def is_valid?
98
+ raise Exception
99
+ end
99
100
 
100
- def get_secret(params)
101
+ def session_key
101
102
  raise Exception
102
103
  end
103
104
 
104
- def init_with_token(auth_token)
105
+ def session_user_id
105
106
  raise Exception
106
107
  end
107
108
 
108
- def session_key=(key)
109
+ def session_expires
109
110
  raise Exception
110
111
  end
111
112
 
112
- def is_activated?
113
+ def session_uid # deprecated
114
+ return session_user_id
115
+ end
116
+
117
+ # SECTION: Protected Abstract Interface
118
+ protected
119
+
120
+ def get_secret(params)
113
121
  raise Exception
114
122
  end
115
123
 
116
- def is_valid?
124
+ def is_activated?
117
125
  raise Exception
118
126
  end
119
127
 
@@ -152,7 +160,7 @@ class FacebookSession
152
160
  params[:v] = "1.0"
153
161
 
154
162
  if (method != "auth.getSession" and method != "auth.createToken")
155
- params[:session_key] = @session_key
163
+ params[:session_key] = session_key
156
164
  params[:call_id] = Time.now.to_f.to_s
157
165
  end
158
166
 
@@ -175,7 +183,7 @@ class FacebookSession
175
183
  @last_call_was_successful = false
176
184
  code = xml.at("error_code").inner_html
177
185
  msg = xml.at("error_msg").inner_html
178
- @last_error = "ERROR #{code}: #{msg} (#{method}, #{params})"
186
+ @last_error = "ERROR #{code}: #{msg} (#{method.inspect}, #{params.inspect})"
179
187
  @last_error_code = code
180
188
 
181
189
  # check to see if this error was an expired session error
@@ -227,16 +235,19 @@ class FacebookSession
227
235
  # Function: param_signature
228
236
  # Generates a param_signature for a call to the API, per the spec on Facebook
229
237
  # see: <http://developers.facebook.com/documentation.php?v=1.0&doc=auth>
230
- def param_signature(params)
238
+ def param_signature(params)
239
+ return generate_signature(params, get_secret(params));
240
+ end
241
+
242
+ def generate_signature(hash, secret)
231
243
 
232
244
  args = []
233
- params.each do |k,v|
245
+ hash.each do |k,v|
234
246
  args << "#{k}=#{v}"
235
247
  end
236
- sorted_array = args.sort
237
- request_str = sorted_array.join("")
238
- param_signature = Digest::MD5.hexdigest("#{request_str}#{get_secret(params)}") # uses Template method get_secret
239
- return param_signature
248
+ sortedArray = args.sort
249
+ requestStr = sortedArray.join("")
250
+ return Digest::MD5.hexdigest("#{requestStr}#{secret}")
240
251
 
241
252
  end
242
253
 
@@ -38,77 +38,169 @@ module RFacebook
38
38
 
39
39
  class FacebookWebSession < FacebookSession
40
40
 
41
- # Function: get_login_url
42
- # Gets the authentication URL
43
- #
44
- # Parameters:
45
- # options.next - the page to redirect to after login
46
- # options.popup - boolean, whether or not to use the popup style (defaults to true)
47
- # options.skipcookie - boolean, whether to force new Facebook login (defaults to false)
48
- # options.hidecheckbox - boolean, whether to show the "infinite session" option checkbox
49
- def get_login_url(options={})
50
- # options
51
- path_next = options[:next] ||= nil
52
- popup = (options[:popup] == nil) ? true : false
53
- skipcookie = (options[:skipcookie] == nil) ? false : true
54
- hidecheckbox = (options[:hidecheckbox] == nil) ? false : true
41
+ # SECTION: Properties
55
42
 
56
- # get some extra portions of the URL
57
- optionalNext = (path_next == nil) ? "" : "&next=#{CGI.escape(path_next.to_s)}"
58
- optionalPopup = (popup == true) ? "&popup=true" : ""
59
- optionalSkipCookie = (skipcookie == true) ? "&skipcookie=true" : ""
60
- optionalHideCheckbox = (hidecheckbox == true) ? "&hide_checkbox=true" : ""
61
-
62
- # build and return URL
63
- return "http://#{LOGIN_SERVER_BASE_URL}#{LOGIN_SERVER_PATH}?v=1.0&api_key=#{@api_key}#{optionalPopup}#{optionalNext}#{optionalSkipCookie}#{optionalHideCheckbox}"
64
- end
43
+ def session_key
44
+ return @session_key
45
+ end
65
46
 
66
- # Function: activate_with_token
67
- # Gets the session information available after current user logs in.
68
- #
69
- # Parameters:
70
- # auth_token - string token passed back by the callback URL
71
- def activate_with_token(auth_token)
72
- result = call_method("auth.getSession", {:auth_token => auth_token})
73
- if result != nil
74
- @session_uid = result.at("uid").inner_html
75
- @session_key = result.at("session_key").inner_html
47
+ def session_user_id
48
+ return @session_uid
76
49
  end
77
- end
78
50
 
79
- # Function: activate_with_previous_session
80
- # Sets the session key directly (for example, if you have an infinite session key)
81
- #
82
- # Parameters:
83
- # key - the session key to use
84
- def activate_with_previous_session(key)
85
- # set the session key
86
- @session_key = key
51
+ def session_expires
52
+ return @session_expires
53
+ end
87
54
 
88
- # determine the current user's id
89
- result = call_method("users.getLoggedInUser")
90
- @session_uid = result.at("users_getLoggedInUser_response").inner_html
91
- end
55
+ # SECTION: URL Getters
56
+
57
+ # Function: get_login_url
58
+ # Gets the authentication URL
59
+ #
60
+ # Parameters:
61
+ # options.next - the page to redirect to after login
62
+ # options.popup - boolean, whether or not to use the popup style (defaults to true)
63
+ # options.skipcookie - boolean, whether to force new Facebook login (defaults to false)
64
+ # options.hidecheckbox - boolean, whether to show the "infinite session" option checkbox
65
+ def get_login_url(options={})
66
+
67
+ # handle options
68
+ nextPage = options[:next] ||= nil
69
+ popup = (options[:popup] == nil) ? false : true
70
+ skipcookie = (options[:skipcookie] == nil) ? false : true
71
+ hidecheckbox = (options[:hidecheckbox] == nil) ? false : true
72
+ frame = (options[:frame] == nil) ? false : true
73
+ canvas = (options[:canvas] == nil) ? false : true
74
+
75
+ # url pieces
76
+ optionalNext = (nextPage == nil) ? "" : "&next=#{CGI.escape(nextPage.to_s)}"
77
+ optionalPopup = (popup == true) ? "&popup=true" : ""
78
+ optionalSkipCookie = (skipcookie == true) ? "&skipcookie=true" : ""
79
+ optionalHideCheckbox = (hidecheckbox == true) ? "&hide_checkbox=true" : ""
80
+ optionalFrame = (frame == true) ? "&fbframe=true" : ""
81
+ optionalCanvas = (canvas == true) ? "&canvas=true" : ""
82
+
83
+ # build and return URL
84
+ return "http://#{WWW_SERVER_BASE_URL}#{WWW_PATH_LOGIN}?v=1.0&api_key=#{@api_key}#{optionalPopup}#{optionalNext}#{optionalSkipCookie}#{optionalHideCheckbox}#{optionalFrame}#{optionalCanvas}"
85
+
86
+ end
87
+
88
+ def get_install_url(options={})
89
+
90
+ # handle options
91
+ nextPage = options[:next] ||= nil
92
+
93
+ # url pieces
94
+ optionalNext = (nextPage == nil) ? "" : "&next=#{CGI.escape(nextPage.to_s)}"
95
+
96
+ # build and return URL
97
+ return "http:/#{WWW_SERVER_BASE_URL}#{WWW_PATH_INSTALL}?api_key=#{@api_key}#{optionalNext}"
98
+
99
+ end
92
100
 
93
- def is_valid?
94
- return (is_activated? and !session_expired?)
95
- end
96
101
 
97
- protected
102
+ # SECTION: Callback Verification Helpers
103
+
104
+ def get_fb_sig_params(originalParams)
105
+
106
+ # setup
107
+ timeout = 48*3600
108
+ namespace = "fb_sig"
109
+ prefix = "#{namespace}_"
110
+
111
+ # get the params prefixed by "fb_sig_" (and remove the prefix)
112
+ sigParams = {}
113
+ originalParams.each do |k,v|
114
+ oldLen = k.length
115
+ newK = k.sub(prefix, "")
116
+ if oldLen != newK.length
117
+ sigParams[newK] = v
118
+ end
119
+ end
120
+
121
+ # handle invalidation
122
+ if (timeout and (sigParams["time"].nil? or (Time.now.to_i - sigParams["time"].to_i > timeout.to_i)))
123
+ # invalidate if the timeout has been reached
124
+ sigParams = {}
125
+ end
126
+
127
+ if !sig_params_valid?(sigParams, originalParams[namespace])
128
+ # invalidate if the signatures don't match
129
+ sigParams = {}
130
+ end
131
+
132
+ return sigParams
133
+
134
+ end
98
135
 
99
- def is_activated?
100
- return (@session_key != nil)
101
- end
102
136
 
103
- # Function: get_secret
104
- # Template method, used by super::signature to generate a signature
105
- def get_secret(params)
137
+
138
+ # SECTION: Session Activation
139
+
140
+ # Function: activate_with_token
141
+ # Gets the session information available after current user logs in.
142
+ #
143
+ # Parameters:
144
+ # auth_token - string token passed back by the callback URL
145
+ def activate_with_token(auth_token)
146
+ result = call_method("auth.getSession", {:auth_token => auth_token})
147
+ if result != nil
148
+ @session_uid = result.at("uid").inner_html
149
+ @session_key = result.at("session_key").inner_html
150
+ @session_expires = result.at("expires").inner_html
151
+ end
152
+ end
153
+
154
+ # Function: activate_with_previous_session
155
+ # Sets the session key directly (for example, if you have an infinite session key)
156
+ #
157
+ # Parameters:
158
+ # key - the session key to use
159
+ def activate_with_previous_session(key, uid=nil, expires=nil)
160
+
161
+ # TODO: what is a good way to handle expiration?
162
+ # low priority since the API will give an error code for me...
163
+
164
+ # set the session key
165
+ @session_key = key
106
166
 
107
- return @api_secret
167
+ # determine the current user's id
168
+ if uid
169
+ @session_uid = uid
170
+ else
171
+ result = call_method("users.getLoggedInUser")
172
+ @session_uid = result.at("users_getLoggedInUser_response").inner_html
173
+ end
174
+
175
+ end
176
+
177
+ def is_valid?
178
+ return (is_activated? and !session_expired?)
179
+ end
180
+
181
+
182
+
183
+
184
+
185
+
186
+ # SECTION: Protected methods
187
+ protected
188
+
189
+ def is_activated?
190
+ return (@session_key != nil)
191
+ end
192
+
193
+ # Function: get_secret
194
+ # Template method, used by super::signature to generate a signature
195
+ def get_secret(params)
196
+ return @api_secret
197
+ end
108
198
 
109
- end
199
+ def sig_params_valid?(sigParams, expectedSig)
200
+ return (sigParams and expectedSig and generate_signature(sigParams, @api_secret) == expectedSig)
201
+ end
110
202
 
111
- end
203
+ end
112
204
 
113
205
 
114
206
 
metadata CHANGED
@@ -1,11 +1,11 @@
1
1
  --- !ruby/object:Gem::Specification
2
- rubygems_version: 0.9.0
2
+ rubygems_version: 0.9.1
3
3
  specification_version: 1
4
4
  name: rfacebook
5
5
  version: !ruby/object:Gem::Version
6
- version: 0.5.1
7
- date: 2007-05-08 00:00:00 -04:00
8
- summary: A Ruby interface to the Facebook API v1.0+
6
+ version: 0.6.0
7
+ date: 2007-05-29 00:00:00 -07:00
8
+ summary: A Ruby interface to the Facebook API v1.0+. Supports the new features from F8.
9
9
  require_paths:
10
10
  - lib
11
11
  email: matt@livelearncode.com
@@ -30,6 +30,7 @@ authors:
30
30
  - Matt Pizzimenti
31
31
  files:
32
32
  - lib/facebook_desktop_session.rb
33
+ - lib/facebook_rails_controller_extensions.rb
33
34
  - lib/facebook_session.rb
34
35
  - lib/facebook_web_session.rb
35
36
  - README