Rails Facebook Development - Sharing some code for tracking active/inactive users…
The next version of FanGamb will be a Facebook app. It’s not a 1:1 translation of the current game to Facebook - we’ll be incorporating many of our game design learnings from running the first version of the game for NFL/NCAA football this fall. Since FanGamb is a Rails app, we’ve been working on setting up a collaborative development process around building Facebook apps in a team environment. I’ll post more on this process to come, but for now, I wanted to contribute some code from our Rails Facebooker-based (that’s the main Rails Facebook API plugin) app.
Documentation on Facebooker is a little light, so there isn’t a whole lot to go on. There is a fairly active Google group, though, and a list of the methods, so we’re not totally in the dark. But as far as hunting for best practices and example code, it’s been a lot of trial and error.
One of the things that we wanted to implement in our application is making sure we know which users have our app installed. It’s pretty common to store the user ids in the app’s database - you need to be able to link it to other data elements that you’re storing for them. However, Facebooker doesn’t by default help you keep that user model in sync with which users actually have the app installed on Facebook. You can tell which of a user’s friends have the app, but not a master list. When you have an active Facebook user session, you can look at User#has_added_app, but we want to be able to tell if a user has the app added even if they aren’t on the app at that moment (for things like leaderboards, etc.).
So, we need to be able to track when a user uninstalls the application and set their user record to be inactive. Facebook has the post_authorize and post_deauthorize call backs for this tracking. As I said, there isn’t a lot of sample Facebooker code out there, so if you’re looking to implement the same thing, here are our callback actions for you to utilize.
The first thing you’re going to want to do is probably put your callback actions in a separate controller, so that you can limit which filters are run on the controller. Facebooker has a before filter ensure_app_installed (suggested for your application_controller) which basically forces a user trying to use the app to install it. This won’t work for your post_deauthorize action, so you need to turn that off:
skip_before_filter :ensure_app_installed, :except => :post_authorize
Also, because Facebook posts to these URLs, Rails treats it as a form submission and will try to verify the authenticity token. So you need to turn that off for both post_authorize and post_deauthorize:
skip_before_filter :verify_authenticity_token
With this setup, you’re able to create the rest of the methods. Here’s the stub code:
class CallbacksController < ApplicationController
skip_before_filter :ensure_app_installed, :except => :post_authorize
skip_before_filter :verify_authenticity_token
def post_authorize
if request.post?
#do something here
render :nothing => true
end
end
def post_deauthorize
if request.post?
#do something here
render :nothing => true
end
end
end
One important point - the above code will work, but we can’t forget to verify the signature to ensure that the request is actually from Facebook, since these actions are outside of the Facebooker plugin (which typically handles this validation).
Looking at the Facebook development wiki, you can see some sample code for RFacebook on how to validate this. Implementing this, our callback controller now looks like the following:
class CallbacksController < ApplicationController
skip_before_filter :ensure_app_installed, :except => :post_authorize
skip_before_filter :verify_authenticity_token
def post_authorize
if request.post?
if verify_uninstall_signature
#set user's has_app indicator to 1 to indicate active
end
end
render :nothing => true
end
def post_deauthorize
if request.post?
if verify_uninstall_signature
#set user's has_app indicator to 0 to indicate inactive
end
end
render :nothing => true
end
private
#based on http://wiki.developers.facebook.com/index.php/Post-Remove_URL
def verify_uninstall_signature
signature = ''
keys = params.keys.sort
keys.each do |key|
next if key == 'fb_sig'
next unless key.include?('fb_sig')
key_name = key.gsub('fb_sig_', '')
signature += key_name
signature += '='
signature += params[key]
end
signature += FACEBOOK_YAML['secret_key']
calculated_sig = Digest::MD5.hexdigest(signature)
if calculated_sig != params[:fb_sig]
logger.warn "\nWARNING :: potential spoofing :: expected signatures did not match"
logger.info "\nSignature (fb_sig param from facebook) :: #{params[:fb_sig]}"
logger.info "\nSignature String (pre-hash) :: #{signature}"
logger.info "\nMD5 Hashed Sig :: #{calculated_sig}"
#check to see if ip variables are nil
if not request.env['HTTP_X_FORWARDED_FOR'].nil? and not request.env['HTTP_X_REAL_IP'].nil?
ip = request.env['HTTP_X_FORWARDED_FOR'] || request.env['HTTP_X_REAL_IP']
else
ip = request.remote_ip
end
logger.info "\nRemote IP :: #{ip}"
return false
else
#logger.warn "\n\nSUCCESS!! Signatures matched.\n"
end
return true
end
end
Get the code in a Gist here.
Note the one line that references FACEBOOK_YAML[‘secret_key’]. This should refer to your secret Facebook key. We put this into the initializer block in environment.rb:
#load facebooker configuration for usage
facebook_config = File.join(RAILS_ROOT, 'config', 'facebooker.yml')
FACEBOOK_YAML = YAML::load(ERB.new(File.read(facebook_config)).result)[RAILS_ENV]
Phew! That should do it - you should now be able to track whether your users have the app installed or not via the post callbacks. Hopefully, this will help someone looking to keep track of active/inactive users in their Rails Facebook app!
Big props to my co-founder Alan deLevie who’s figured out a big chunk of this Facebooker stuff for the FanGamb team.
EDIT: typo fixed. Thanks Sting Tao!