Module: Facebooker2::Rails::Controller

Defined in:
lib/facebooker2/rails/controller.rb

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.included(controller) ⇒ Object



7
8
9
10
11
12
# File 'lib/facebooker2/rails/controller.rb', line 7

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

Instance Method Details

#base64_url_decode(encoded) ⇒ Object



237
238
239
240
241
# File 'lib/facebooker2/rails/controller.rb', line 237

def base64_url_decode(encoded)
  chars_to_add = 4-(encoded.size % 4)
  encoded += ("=" * chars_to_add)
  Base64.decode64(encoded.tr("-_", "+/"))
end

#current_facebook_clientObject



23
24
25
26
27
28
29
30
# File 'lib/facebooker2/rails/controller.rb', line 23

def current_facebook_client
  if (Facebooker2.oauth2)
    oauth2_fetch_client_and_user
  else
    fetch_client_and_user
  end
  @_current_facebook_client
end

#current_facebook_userObject



14
15
16
17
18
19
20
21
# File 'lib/facebooker2/rails/controller.rb', line 14

def current_facebook_user
  if (Facebooker2.oauth2)
    oauth2_fetch_client_and_user
  else
    fetch_client_and_user
  end
  @_current_facebook_user
end

#facebook_paramsObject



127
128
129
# File 'lib/facebooker2/rails/controller.rb', line 127

def facebook_params
  @facebook_param ||= fb_load_facebook_params
end


90
91
92
# File 'lib/facebooker2/rails/controller.rb', line 90

def fb_cookie
  cookies[fb_cookie_name]
end

#fb_cookie?Boolean

Returns:

  • (Boolean)


86
87
88
# File 'lib/facebooker2/rails/controller.rb', line 86

def fb_cookie?
  !fb_cookie.blank?
end


75
76
77
78
79
80
81
82
83
84
# File 'lib/facebooker2/rails/controller.rb', line 75

def fb_cookie_hash
  return nil unless fb_cookie?
  hash={}
  data = fb_cookie.gsub(/"/,"")
  data.split("&").each do |str|
    parts = str.split("=")
    hash[parts.first] = parts.last
  end
  hash
end


94
95
96
# File 'lib/facebooker2/rails/controller.rb', line 94

def fb_cookie_name
  return "#{Facebooker2.cookie_prefix + Facebooker2.app_id.to_s}"
end

check if the expected signature matches the one from facebook

Returns:

  • (Boolean)


99
100
101
# File 'lib/facebooker2/rails/controller.rb', line 99

def fb_cookie_signature_correct?(hash,secret)
  generate_signature(hash,secret) == hash["sig"]
end

#fb_create_user_and_client(token, expires, userid) ⇒ Object



62
63
64
65
66
# File 'lib/facebooker2/rails/controller.rb', line 62

def fb_create_user_and_client(token,expires,userid)
  client = Mogli::Client.new(token,expires.to_i)
  user = Mogli::User.new(:id=>userid)
  (user,client)
end

#fb_load_facebook_paramsObject



131
132
133
134
135
136
# File 'lib/facebooker2/rails/controller.rb', line 131

def fb_load_facebook_params
  return {} if params[:signed_request].blank?
  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

#fb_sign_in_user_and_client(user, client) ⇒ Object



68
69
70
71
72
73
# File 'lib/facebooker2/rails/controller.rb', line 68

def (user,client)
  user.client = client
  @_current_facebook_user = user
  @_current_facebook_client = client
  @_fb_user_fetched = true
end

#fb_signed_request_json(encoded) ⇒ Object



121
122
123
124
125
# File 'lib/facebooker2/rails/controller.rb', line 121

def fb_signed_request_json(encoded)
  chars_to_add = 4-(encoded.size % 4)
  encoded += ("=" * chars_to_add)
  base64_url_decode(encoded)
end

#fb_signed_request_sig_valid?(sig, encoded) ⇒ Boolean

Returns:

  • (Boolean)


138
139
140
141
142
143
# File 'lib/facebooker2/rails/controller.rb', line 138

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

#fetch_client_and_userObject

This mimics the getSession logic from the php facebook SDK github.com/facebook/php-sdk/blob/master/src/facebook.php#L333



35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
# File 'lib/facebooker2/rails/controller.rb', line 35

def fetch_client_and_user
  return if @_fb_user_fetched
  # Try to authenticate from the signed request first
  sig = fetch_client_and_user_from_signed_request
  sig = fetch_client_and_user_from_cookie if @_current_facebook_client.nil? and !signed_request_from_logged_out_user?
  
  #write the authentication params to a new cookie
  if !@_current_facebook_client.nil? 
    #we may have generated the signature based on the params in @facebook_params, and the expiration here is different
    
    set_fb_cookie(@_current_facebook_client.access_token, @_current_facebook_client.expiration, @_current_facebook_user.id, sig)
  else
    # if we do not have a client, delete the cookie
    set_fb_cookie(nil,nil,nil,nil)
  end
  
  @_fb_user_fetched = true
end


54
55
56
57
58
59
60
# File 'lib/facebooker2/rails/controller.rb', line 54

def fetch_client_and_user_from_cookie
  if (hash_data = fb_cookie_hash) and
    fb_cookie_signature_correct?(fb_cookie_hash,Facebooker2.secret)
    fb_create_user_and_client(hash_data["access_token"],hash_data["expires"],hash_data["uid"])
    return fb_cookie_hash["sig"]
  end
end

#fetch_client_and_user_from_signed_requestObject



145
146
147
148
149
150
151
152
153
154
155
# File 'lib/facebooker2/rails/controller.rb', line 145

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])
    
    if @_current_facebook_client
      #compute a signature so we can store it in the cookie
      sig_hash = Hash["uid"=>facebook_params[:user_id],"access_token"=>facebook_params[:oauth_token],"expires"=>facebook_params[:expires]]
      return generate_signature(sig_hash, Facebooker2.secret)
    end
  end
end

#generate_signature(hash, secret) ⇒ Object

compute the md5 sig based on access_token,expires,uid, and the app secret



110
111
112
113
114
115
116
117
118
119
# File 'lib/facebooker2/rails/controller.rb', line 110

def generate_signature(hash,secret)
  sorted_keys = hash.keys.reject {|k| k=="sig"}.sort
  test_string = ""
  sorted_keys.each do |key|
    test_string += "#{key}=#{hash[key]}"
  end
  test_string += secret
  sig = Digest::MD5.hexdigest(test_string)
  return sig
end

#oauth2_current_facebook_userObject

Oauth2



214
215
216
217
# File 'lib/facebooker2/rails/controller.rb', line 214

def oauth2_current_facebook_user
  oauth2_fetch_client_and_user
  @_current_facebook_user
end

#oauth2_fetch_client_and_userObject



219
220
221
222
223
# File 'lib/facebooker2/rails/controller.rb', line 219

def oauth2_fetch_client_and_user
  return if @_fb_user_fetched
  sig = oauth2_fetch_client_and_user_from_cookie if @_current_facebook_client.nil?
  @_fb_user_fetched = true
end


225
226
227
228
229
230
231
232
233
234
# File 'lib/facebooker2/rails/controller.rb', line 225

def oauth2_fetch_client_and_user_from_cookie
  return unless fb_cookie?
  sig,payload = fb_cookie.split('.')
  return unless fb_signed_request_sig_valid?(sig, payload)
  data = JSON.parse(base64_url_decode(payload))
  authenticator = Mogli::Authenticator.new(Facebooker2.app_id, Facebooker2.secret, '')
  client = Mogli::Client.create_from_code_and_authenticator(data["code"], authenticator)
  user = Mogli::User.new(:id=>data["user_id"])
  (user, client)
end

/**

This method was shamelessly stolen from the php facebook SDK:
https://github.com/facebook/php-sdk/blob/master/src/facebook.php

 Set a JS Cookie based on the _passed in_ session. It does not use the
 currently stored session -- you need to explicitly pass it in.

If a nil access_token is passed in this method will actually delete the fbs_ cookie

*/


168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
# File 'lib/facebooker2/rails/controller.rb', line 168

def set_fb_cookie(access_token,expires,uid,sig) 
  
  #default values for the cookie
  value = 'deleted'
  expires = Time.now.utc - 3600 unless expires != nil

  # If the expires value is set to some large value in the future, then the 'offline access' permission has been
  # granted.  In the Facebook JS SDK, this causes a value of 0 to be set for the expires parameter.  This value 
  # needs to be correct otherwise the request signing fails, so if the expires parameter retrieved from the graph
  # api is more than a year in the future, then we set expires to 0 to match the JS SDK.
  expires = 0 if expires > Time.now + 1.year
  
  if access_token
    # Retrieve the existing cookie data
    data = fb_cookie_hash || {}
    # Remove the deleted value if this has previously been set, as we don't want to include it as part of the 
    # request signing parameters
    data.delete('deleted') if data.key?('deleted')
    # Keep existing cookie data that could have been set by FB JS SDK
    data.merge!('access_token' => access_token, 'uid' => uid, 'sig' => sig, 'expires' => expires.to_i.to_s)
    # Create string to store in cookie
    value = '"'
    data.each do |k,v|
      value += "#{k.to_s}=#{v.to_s}&"
    end
    value.chop!
    value+='"'
  end
  
  # if an existing cookie is not set, we dont need to delete it
  if (value == 'deleted' && (!fb_cookie? || fb_cookie == "" ))
    return;
  end
  
  #My browser doesn't seem to save the cookie if I set expires
  cookies[fb_cookie_name] = { :value=>value }#, :expires=>expires}
end

#set_p3p_header_for_third_party_cookiesObject

For canvas apps, You need to set the p3p header in order to get IE 6/7 to accept the third-party cookie For details www.softwareprojects.com/resources/programming/t-how-to-get-internet-explorer-to-use-cookies-inside-1612.html



209
210
211
# File 'lib/facebooker2/rails/controller.rb', line 209

def set_p3p_header_for_third_party_cookies
  response.headers['P3P'] = 'CP="IDC DSP COR ADM DEVi TAIi PSA PSD IVAi IVDi CONi HIS OUR IND CNT"'
end

#signed_request_from_logged_out_user?Boolean

If the signed request is valid but contains no oauth token, the user is either logged out from Facebook or has not authorized the app

Returns:

  • (Boolean)


105
106
107
# File 'lib/facebooker2/rails/controller.rb', line 105

def signed_request_from_logged_out_user?
  !facebook_params.empty? && facebook_params[:oauth_token].nil?
end