Run with Friends is a sample Facebook Canvas Application written in Python running on Google App Engine. The application is designed to run within an iframe on a Facebook Canvas page.

Facebook provides a large array of integration points for applications and this sample application makes use of only a few of those capabilities. Some of the things that are covered here are:
You can try the sample at http://apps.facebook.com/runwithfriends/
In order to run the sample, you need the following:
In the Web Site section:
http://localhost:8080/In the Facebook Integration section:
http://localhost:8080/. This will get used as the iframe src.mkdir runwithfriends
cd runwithfriends
curl -L http://github.com/fbsamples/web-runwithfriends/tarball/master |
tar xzvf - --strip-components=1
Configure the local settings for your app. Start by copying
conf.py.example to conf.py, then edit the various values in the
conf.py:
cp conf.py.example conf.py
vim conf.py
Add the application to the App Engine Launcher and start it (or start it from the command line):
dev_appserver.py .
Go to localhost:8080
One of the main benefits of integrating with Facebook is the ease with which you can use the user's real identity provided by Facebook, and the wealth of existing user data that is made available. For the sample application, we used a few different pieces of data to represent the User:
The first step to getting this information is having the user
authorize your application
and grant it the necessary
permissions.
The result of granting these permissions gives you the access_token which
enables access to the APIs that will
give you the above data.
In the sample application we've opted to represent all this information as part of the User model, and is defined as:
class User(db.Model):
user_id = db.StringProperty(required=True)
access_token = db.StringProperty(required=True)
name = db.StringProperty(required=True)
picture = db.StringProperty(required=True)
email = db.StringProperty()
friends = db.StringListProperty()
dirty = db.BooleanProperty()
The typical flow for a user when using the application begins with the user
landing at some Canvas URL, like
http://apps.facebook.com/runwithfriends/.
At this point, Facebook will load up its chrome, and render a <iframe> tag
to your application. You'll notice there isn't a src specified. Using some
JavaScript and the <form> tag, Facebook triggers a POST request to your
application. This is done for security reasons, as the sensitive user data
won't be sent via the HTTP Referrer header as long it's sent as POST data.
Additionally, in order to ensure the data Facebook sends over is not tampered
with, it provides a signed JSON data structure, which is signed using the
application secret. The parameter is called
signed_request,
and sample application uses this to load it:
def load_signed_request(self, signed_request):
"""Load the user state from a signed_request value"""
sig, payload = signed_request.split(u'.', 1)
sig = self.base64_url_decode(sig)
data = json.loads(self.base64_url_decode(payload))
expected_sig = hmac.new(
self.app_secret, msg=payload, digestmod=hashlib.sha256).digest()
# allow the signed_request to function for upto 1 day
if sig == expected_sig and data[u'issued_at'] > (time.time() - 86400):
self.signed_request = data
self.user_id = data.get(u'user_id')
self.access_token = data.get(u'oauth_token')
When Facebook sends the initial POST request, if it detects that the user has
already authorized your application, it will include the User ID and the
access_token as part of the signed_request parameter. The application uses
this information, and sets an internal auth cookie named u. We choose to use
the same format as for the cookie value as the signed_request parameter in
order to simplify/reuse our authentication code. The signed_request parameter
is documented at length in the official
documentation.
The sample application uses this to initialize the Facebook instance, and if possible the User instance on every request:
def init_facebook(self):
"""Sets up the request specific Facebook and User instance"""
facebook = Facebook()
user = None
# initial facebook request comes in as a POST with a signed_request
if u'signed_request' in self.request.POST:
facebook.load_signed_request(self.request.get('signed_request'))
self.request.method = u'GET' # causes loss of request.POST data
self.set_cookie(
'u', facebook.user_cookie, datetime.timedelta(minutes=1440))
elif 'u' in self.request.cookies:
facebook.load_signed_request(self.request.cookies.get('u'))
# try to load or create a user object
if facebook.user_id:
user = User.get_by_key_name(facebook.user_id)
if user:
# update stored access_token
if facebook.access_token and \
facebook.access_token != user.access_token:
user.access_token = facebook.access_token
user.put()
# refresh data if we failed in doing so after a realtime ping
if user.dirty:
user.refresh_data()
# restore stored access_token if necessary
if not facebook.access_token:
facebook.access_token = user.access_token
if not user and facebook.access_token:
me = facebook.api(u'/me', {u'fields': u'picture,friends'})
logging.info(u'/me API call response: ' + unicode(me))
user = User(key_name=facebook.user_id,
user_id=facebook.user_id,
access_token=facebook.access_token,
name=me[u'name'],
email=me.get(u'email'), # optional
picture=me[u'picture'],
friends=[user[u'id'] for user in me[u'friends'][u'data']])
user.put()
self.facebook = facebook
self.user = user
There are a several important decisions here that ensure a smooth experience for users:
signed_request sent in the POST rather than a cookie
value. If we don't find signed_request in the POST data, we lookup the
cookie value instead.POST that includes a signed_request. This
ensures we persist the last well known user state.GET when we get a POST with the
signed_request. Facebook will always send a POST on the initial iframe
request for security reasons, and this can be to any end-point in our
application, while in reality, this should be treated as a GET. By
resetting the method to GET, we make the rest of our application ignorant
of this behaviour.signed_request format in the cookie, this is not a
Facebook requirement. You could use your own session storage without any
consequences, and all that is required is that you store the Facebook
user_id to identify the logged in user. Just remember to prefer the
signed_request in POST if it's there and update your session the same
way as the cookie is updated.signed_request value we get in the POST as the
cookie because it will contain the access_token and may be large.
Instead, we simply store the user_id in the cookie, and load the stored
access_token from the user object as needed.signed_request in the POST, we update the stored
access_token with the freshly received one if it isn't the same, always
assuming it's the better one of the two.access_token,
we'll create the user object.dirty bit is used along with the Real-time
API which is described
further below.Authentication with Facebook involves sending the user to facebook.com and
having Facebook prompt them to authorize the application. The simplest way to
do so is to redirect the user's browser to facebook.com with some parameters,
and have the user come back after they've taken an action. If you want to go
the manual route, the documentation for authenticating users in a web
application
will provide the easiest path. In the sample application we've opted to use the
login with faces
plugin via the
JavaScript SDK to
provide a richer experience. The plugin will show a login button, and the faces
of the friends who have used the application (if possible). The data is not
actually made available to the application yet, the social context is provided
by using an iframe to facebook.com. This is done via XFBML, a client side
markup language supported by the JavaScript
SDK:
<fb:login-button perms="email,offline_access"
show-faces="true"></fb:login-button>
The result of which is:

Notice the perms attribute on the tag -- this lists the extended
permissions
we'll need the user to grant our application in order for it to function.
Upon clicking the Login button, the user will be shown a popup dialog to
grant your application the requested permissions:

The sample application requests extended permissions, but doesn't actually need it! Ideally you should ask for no additional permissions when the user initially authorizes your application, and keep the list of things in the above dialog short. You can ask for additional permissions even after the user has already authorized your application for basic information. Requesting minimal permissions will mean a greater initial conversion, giving you the opportunity to provide the user with an experience which makes for a more compelling backdrop when you ask for the additional permissions at a later point in time.
JavaScript is a heavily event driven environment, and the JavaScript
SDK embraces that
nature and provides its own events. Notably, there are two useful event's we
care about in the authentication context: auth.login and auth.logout. The
JavaScript SDK
fires these in response to various interactions, including a login action from
the <fb:login-button>. These can be subscribed to as:
FB.Event.subscribe('auth.login', function(response) {
// Reload the entire page. Could also do an Ajax request and dynamically
// update the already loaded page.
window.location.reload(true);
});
FB.Event.subscribe('auth.logout', function() {
// Do something...
});
These events let you do some custom work whenever a relevant change happens.
Remember to be careful about when you subscribe to these events. It's usually
ideal to invoke
FB.Event.subscribe()
right after the
FB.init()
call. For most applications which do the heavy lifting on the server side,
you'll typically just want to reload the top frame and have Facebook send you a
signed_request with the user's credentials, and let the server render the
logged in view. This is what the sample application does:
function handleSessionChange(response) {
if ((Config.userIdOnServer && !response.session) ||
Config.userIdOnServer != response.session.uid) {
top.location = 'http://apps.facebook.com/' + Config.canvasName + '/';
}
}
FB.Event.subscribe('auth.sessionChange', handleSessionChange);
The sample application also handles in-flight user changes. It does this by
comparing the user_id of the logged in user when the server rendered the page
(or lack there of) with the user_id based on the auth events in JavaScript
(which is doing a live check against facebook.com and is more accurate out of
the two).
Some browsers will let iframes set cookies based on the presence of the P3P header. Notably, IE respects this header. Ideally you may want to look up the right value based on the privacy policy adopted by your application, but any value will usually suffice. The sample application sends this for instance:
P3P: CP="HONK"
The Graph API provides a RESTful interface to the Facebook API. The API makes a wealth of information available, assuming you've requested the necessary extended permissions. The basic access pattern to use the API is:
https://graph.facebook.com/[PATH]?access_token=[TOKEN]&[PARAMS...]
The Facebook class provides a simple interface to access the Facebook API. This may not be sufficient for more advance use cases (such as file uploads):
def api(self, path, params=None, method=u'GET', domain=u'graph'):
"""Make API calls"""
if not params:
params = {}
params[u'method'] = method
if u'access_token' not in params and self.access_token:
params[u'access_token'] = self.access_token
result = json.loads(urlfetch.fetch(
url=u'https://' + domain + u'.facebook.com' + path,
payload=urllib.urlencode(params),
method=urlfetch.POST,
headers={
u'Content-Type': u'application/x-www-form-urlencoded'}).content)
if isinstance(result, dict) and u'error' in result:
raise FacebookApiError(result)
return result
Note, here we're using the method override, that is, specifying the method as
a parameter to specify the intended HTTP method as a convenience.
Once we have a Facebook user_id and access_token, we can make use of that
to populate our user object using the API method described above. Here's what
the sample application does to fetch the date we care about. Notice we request
the additional picture field, as well as the friends data as part of the single
API call. Additionally, you'll notice email is not actually required, but
will be populated if it's available. We do not need to do the same for name,
picture and friends as that is part of the minimal set of information made
available when a user authorizes your application.
me = self.facebook.api(u'/me', {u'fields': u'picture,friends'})
logging.info(u'/me API call response: ' + unicode(me))
user = User(key_name=self.facebook.user_id,
user_id=self.facebook.user_id,
access_token=self.facebook.access_token,
name=me[u'name'],
email=me.get(u'email'), # optional
picture=me[u'picture'],
friends=[user[u'id'] for user in me[u'friends'][u'data']])
user.put()
The Graph API provides a simple interface to a wealth of facebook data. The sample application uses only some basic user information, including the user's friends list, but after requesting the right set of permissions, you can access the user's photos, events, groups, checkins and a lot more.
Once the user successfully posts a run, we want to provide them with the option to post a story to Facebook. Again, you could manually popup a window to the dialog, we can provide a richer user experience by making use of the JavaScript SDK and making use of FB.ui().
Here's what the sample application uses to publish the story:
function publishRun(title) {
FB.ui({
method: 'stream.publish',
attachment: {
name: title,
caption: "I'm running!",
media: [{
type: 'image',
href: 'http://runwithfriends.appspot.com/',
src: 'http://runwithfriends.appspot.com/splash.jpg'
}]
},
action_links: [{
text: 'Join the Run',
href: 'http://runwithfriends.appspot.com/'
}],
user_message_prompt: 'Tell your friends about the run:'
});
}
After the user adds a Run, we show the user a confirmation message with a link that triggers this function, and results in an experience like such:

The stream publish dialog provides an easy, intentional user action that allows your application to post content back to Facebook. The API allows you to specify various attributes that control the display and the behaviour of the posts. As this process already involves an explicit user interaction, you are able to make use of this without permissions of any sort. In fact, you can show the popup version of the stream publish dialog even before the user has authorized your application.
Our approach to use the basic user data is to make an API call to Facebook and cache the data in our application's local data store. We also subscribe to the Real-time API which notifies us when the data we care about changes, and App Engine provided Task Queues to manage the updates that should be triggered based on the pings. The two together allow us to provide a vastly improved user experience as we no longer need to make expensive HTTP calls as the user is using the application, but yet always have the most up-to-date information about the user including their name, email, profile picture and list of friends as long as the user is actively using our application.
We use a two fold approach here. We store the access_token we get when the
user visits the application, and will try to use that to make an API call to
refresh the user data in a background task when we are notified of changes. If
this API call fails, we'll flip the dirty bit on the user instance. This will
mean we'll use some stale data for the user, until they visit the application
the next time around at which point we'll have a fresh access_token, and
we'll notice the dirty bit and trigger a refresh of the data. We use the two
fold system despite the fact that we ask for the offline_access permission
(which in theory means indefinite access). It is always a good idea to handle
the case where the stored access_token is no longer valid, which can happen
for various reasons (user explicitly revoked access, token expired and a few
others).
The process of using the
Real-time API begins with
subscribing to object families. Our application subscribes to the user
object, and specifically the ['email', 'name', 'picture', 'friends'] fields.
For our sample application, we've setup an internal end-point that enables us
to trigger the subscription setup flow. Simply visit
http://localhost:8080/realtime?setup=1.
If you get an error message, ensure you've added your own Facebook User ID to
the ADMIN_USER_IDS in conf.py, and that you're logged into the application.
This is our poor mans admin system.
The sample application uses this to setup the subscription:
def setup_subscription(self):
path = u'/' + conf.FACEBOOK_APP_ID + u'/subscriptions'
params = {
u'access_token': conf.FACEBOOK_APP_ID + u'|' +
conf.FACEBOOK_APP_SECRET,
u'object': u'user',
u'fields': u'name,email,picture,friends',
u'callback_url': conf.EXTERNAL_HREF + u'realtime',
u'verify_token': conf.FACEBOOK_REALTIME_VERIFY_TOKEN,
}
response = self.facebook.api(path, params, u'POST')
logging.info(u'Real-time setup API call response: ' + unicode(response))
Here we're making an application secret enabled call, as can be seen by the
fact that we're using application ID and the application secret as the
access_token. We are informing Facebook's
Real-time API service about
our configuration, including the objects and fields we want to get change
notifications about, as well as the endpoint to hit. Be careful, you'll
need to ensure the EXTERNAL_HREF has been configured correctly, and is a
end-point that can be reached from anywhere on the internet. This means you
cannot use something like http://localhost:8080/ here, because that is not
an end-point Facebook's servers can reach. Once you've triggered the setup,
you'll receive a background ping from Facebook's servers to confirm the
subscription after which you'll start receiving change notifications.
Here's the top level entry point for our Real-time API change notifications:
def post(self):
body = self.request.body
logging.info(u'Real-time ping: ' + body)
if self.request.headers[u'X-Hub-Signature'] != (u'sha1=' + hmac.new(
self.facebook.app_secret,
msg=body,
digestmod=hashlib.sha1).hexdigest()):
logging.error(
u'Real-time signature check failed: ' + unicode(self.request))
return
data = json.loads(body)
if data[u'object'] == u'user':
for entry in data[u'entry']:
taskqueue.add(url=u'/task/refresh-user/' + entry[u'id'])
logging.info('Added task to queue to refresh user data.')
else:
logging.warn(u'Unhandled Real-time ping: ' + body)
First we ensure that the signature of the ping matches can be verified based on our application secret. As we're only subscribing to user object changes, we have only implemented the same, and log a warning if another type of notification is received.
The other interesting aspect here is that we may get batches of changes, as
represented by the entry property in the notification, which is an array of
objects. The Real-time API needs confirmation when you've handled the ping.
Success is indicated by simply returning a 200 response within 10 seconds. This
coupled with the large batch sizes means we cannot do the work of refreshing
the user data in the same request that receives the Real-time ping as we would
not have enough time. Additionally, App Engine itself restricts each request to
30 seconds or so, which would be another limitation. So we simply queue up
independent tasks for each user_id mentioned in the ping, and do the work of
refreshing the data in the background task. Ideally, we'll be successful in
refreshing the data, but if we're not, we mark the user instance as dirty to
trigger a refresh the next time the user visits our application.
class RefreshUserHandler(BaseHandler):
"""Refresh user data using if possible."""
def post(self, user_id):
logging.info('Refreshing user data for ' + user_id)
user = User.get_by_key_name(user_id)
if not user:
return
try:
user.refresh_data()
except FacebookApiError:
user.dirty = True
user.put()
This process keeps our internal copy of the user data up-to-date, and essentially enables us to limit ourselves to invoking the Facebook Graph API only at sign-up time and when we notice changes, and using cached data for the rest of the page loads.