Showing posts with label facebookapp. Show all posts
Showing posts with label facebookapp. Show all posts

Wednesday, July 6, 2011

Follow-up on Hello World Facebook App

Introduction

As a proof that I'm still learning how to make a Facebook App, I found some flaws in my previous post. The flaw is that it only works for cases wherein the user accessing the app has already authorized the app. I discovered this when I tinkered with test users in facebook. So below is the updated version.

Lets Digress, How to Create Facebook Test Users

I only discovered the problem with the first version of the application after testing it with some test users. Thus in my opinion, it is very, very important that we put importance to the creation of test users to be able to test our applications properly.

First we need to generate an access token.


https://graph.facebook.com/oauth/access_token?
     client_id=YOUR_APP_ID&client_secret=YOUR_APP_SECRET&
     grant_type=client_credentials

You can use curl or your browser to make a GET request to the URL above. I just used  a chrome browser. Use the access token to generate the test user by making a request to the following URL.

https://graph.facebook.com/APP_ID/accounts/test-users?
  installed=false
  &permissions=read_stream
  &method=post
  &access_token=APP_ACCESS_TOKEN

The generated test user is specific to the application being tested so you have to provide some information regarding the application to be tested in the URL above.  Also take note of the 'installed' parameter. If it is set to 'false', it means that the generated user has not yet installed/authorized the application. Making a GET request to the above URL would return a JSON string similar to the following.


{
   "id": "11111111",
   "login_url": "https://www.facebook.com/platform/test_account_login.php?user_id=11111111&n=pU4qXlyHOobnAX7",
   "email": "ebbzkuk_huiman\u0040tfbnw.net",
   "password": "1111111"
}

To use the generated user, simply cut and paste the login_url information to the navigation bar of a browser and you can start testing your application.






Updated Hello World Program


import tornado.ioloop
import tornado.web


import base64
import json
import hmac
import hashlib
import time
import urllib2
import urllib


APP_SECRET = '####'
APP_ID = '####'


def base64_url_decode(data):
    data = data.encode(u'ascii')
    data += '=' * (4 - (len(data) % 4))
    return base64.urlsafe_b64decode(data)


def load_signed_request(signed_request, app_secret):
    try:
        sig, payload = signed_request.split(u'.', 1)
        sig = base64_url_decode(sig)
        data = json.loads(base64_url_decode(payload))


        expected_sig = hmac.new(app_secret, msg=payload, digestmod=hashlib.sha256).digest()


        if sig == expected_sig and data[u'issued_at'] > (time.time() - 86400):
            return data
        else:
            return None
    except ValueError, ex:
        return None


class MainHandler(tornado.web.RequestHandler):
    def get(self):
        self.write("Hello, world")


    def post(self):
        signed_request = self.get_argument('signed_request')
        data = load_signed_request(signed_request, APP_SECRET)


        if data.get('user_id'):
            user_id = data.get(u"user_id")
            url = 'https://graph.facebook.com/%s'%user_id
            fd = urllib2.urlopen(url)
            response = fd.read()
            fd.close()
            userdata = json.loads(response)
            self.write('Hello %s\n'%userdata['name'])
        else:
            canvasurl = urllib.quote("http://apps.facebook.com/nielsampleapp/")
            url = "https://www.facebook.com/dialog/oauth?client_id=%s&redirect_uri=%s&scope=email,read_stream"%(APP_ID, canvasurl)
            self.write('<script> top.location.href="%s"</script>'%url)


application = tornado.web.Application([
    (r"/", MainHandler),
])


if __name__ == "__main__":
    application.listen(8888)
    tornado.ioloop.IOLoop.instance().start()

The main difference in this version is that it has integrated some code for authorization. Remember that when Facebook loads the application, it makes a POST request passing a 'signed_request' parameter. The 'signed_request' parameter contains is a signed JSON string containing information about the user accessing the application.


        signed_request = self.get_argument('signed_request')
        data = load_signed_request(signed_request, APP_SECRET)

If the user has already authorized the application, the JSON string would be similar to the following JSON string.

{u'user_id': u'11111111', u'algorithm': u'HMAC-SHA256', u'expires': 1309536000, u'oauth_token': u'############', u'user': {u'locale': u'en_US', u'country': u'ph', u'age': {u'min': 21}}, u'issued_at': 1309531299}

If the user has not authorized the application, the JSON string would be similar to the following JSON string.


{u'issued_at': 1309957831, u'user': {u'locale': u'en_US', u'country': u'ph', u'age': {u'min': 21}}, u'algorithm': u'HMAC-SHA256'}

The main difference between the JSON strings above is that one has personal information, while the other doesn't. Because the main reason for making Facebook Apps is to provide a personalized user experience, it is imperative that the application should prompt the user for authorization. This is done in the following code.

                   if data.get('user_id'):
            user_id = data.get(u"user_id")
            url = 'https://graph.facebook.com/%s'%user_id
            fd = urllib2.urlopen(url)
            response = fd.read()
            fd.close()
            userdata = json.loads(response)
            self.write('Hello %s\n'%userdata['name'])
        else:
            canvasurl = urllib.quote("http://apps.facebook.com/nielsampleapp/")
            url = "https://www.facebook.com/dialog/oauth?client_id=%s&redirect_uri=%s&scope=email,read_stream"%(APP_ID, canvasurl)
            self.write('<script> top.location.href="%s"</script>'%url)

To detect whether the user has already authorized the application, the existence of the 'user_id' parameter is checked in the passed JSON data. If 'user_id' does not exist, it means that the user has not yet authorized the application. To facilitate the authorization process, the application redirects the user to a Facebook dialog which handles the authorization process.

            canvasurl = urllib.quote("http://apps.facebook.com/nielsampleapp/")
            url = "https://www.facebook.com/dialog/oauth?client_id=%s&redirect_uri=%s&scope=email,read_stream"%(APP_ID, canvasurl)
            self.write('<script> top.location.href="%s"</script>'%url)




After authorizing the application, the user is redirected back to the application.

Friday, July 1, 2011

Hello World Facebook App (Python Version)

Introduction

Note: This application will only work if the user accessing the app is logged in and has already installed the facebook app. This would also work if the user accessing the app is the creator/developer of the app. I would make a follow-up post to make this app more general.

I started reading on Facebook Apps days ago. I was quite interested to discover the capabilities of this framework. It might come in handy later on.

So after reading the documentation and browsing the example Apps, I was not quite confident that I understood things. I remembered that every time I tried to learn a programming language, all the books/tutorials that I have read always started with a simple "Hello World" program. This program sort of like breaks the ice and gives you a feeling of accomplishment afterwards. There is something about you actually doing the coding and seeing the result of your code that makes you feel more confident, happy, even ecstatic afterwards.

In this blog post, I will discuss how I made a simple "Hello World" program for Facebook Apps. I plan to extend this in succeeding posts, add more functionality. But for now, I'll try to keep it as simple as possible so as to ease my entry into this new framework. I'm sharing this experience as this might also be useful to others trying to learn Facebook Apps.

Facebook Canvas

A Facebook App is just like any other web application. The only difference is that it is run inside an iframe in Facebook. Because of this, you could use almost any web application framework. You have the liberty to choose the programming language, database, web server, etc. The iframe in which the Facebook App is loaded is referred to as the Facebook Canvas.

When your web application/Facebook App is loaded into the iframe, the web application is requested using the POST method rather than the GET method. It is requested using the POST method as Facebook passes some POST parameters that identifies the Facebook user loading the application. This becomes clear in our "Hello World" application.

Create the Facebook App

To create the Facebook App, click on the "Create New App" button at the top right-hand corner of this page, https://developers.facebook.com/apps. Just fill in the requested information and you will have your new application. The most important thing to take note of is the canvas url as shown below.


In this case, I set the "Canvas URL" to a local address of a machine that runs the web server. Actually, this is an address of an Ubuntu Server that runs in Virtualbox. If you are running the web server locally in your computer, you can use "http://localhost:8888/" instead.

Tornado Webserver

Because of its simplicity and my preference, I would be using the Tornado Webserver. Its setup is quite easy just follow the documentation and you'll have the server ready in no time. You can easily install it using:

$ easy_install tornado

Initial Program

After installing Tornado, create the main.py script with the following code.


import tornado.ioloop
import tornado.web


class MainHandler(tornado.web.RequestHandler):
    def post(self):
        self.write("Hello, world")


application = tornado.web.Application([
    (r"/", MainHandler),
])


if __name__ == "__main__":
    application.listen(8888)
    tornado.ioloop.IOLoop.instance().start()

To understand the code better, it would be nice if you could go through the first few sections of the Tornado documentation. 

The "MainHandler" class above handles requests made to the root, '/', of the webserver. The "post" method of the class responds to POST requests. Notice that we don't have a "get" method as the application will be requested using the POST method as mentioned earlier.

Running this webserver, using the command below

$ python main.py

would allow as to access the Facebook app. The app is accessible through the url below.

http://apps.facebook.com/[appname]/

Replace the [appname], with the app name you assigned to your app. This would produce the following output.

Better Hello World

There we have a basic "Hello World" app. But honestly, it does not feel that it exposes even a itsy-bitsy Facebook functionality. So lets improve it.


import tornado.ioloop
import tornado.web


import base64
import json
import hmac
import hashlib
import time
import urllib2


def base64_url_decode(data):
    data = data.encode(u'ascii')
    data += '=' * (4 - (len(data) % 4))
    return base64.urlsafe_b64decode(data)


def load_signed_request(signed_request, app_secret):
    try:
        sig, payload = signed_request.split(u'.', 1)
        sig = base64_url_decode(sig)
        data = json.loads(base64_url_decode(payload))


        expected_sig = hmac.new(app_secret, msg=payload, digestmod=hashlib.sha256).digest()


        if sig == expected_sig and data[u'issued_at'] > (time.time() - 86400):
            return data
        else:
            return None
    except ValueError, ex:
        return None


class MainHandler(tornado.web.RequestHandler):
    def get(self):
        self.write("Hello, world")


    def post(self):
        app_secret = '############' # put your app secret here
        signed_request = self.get_argument('signed_request')
        data = load_signed_request(signed_request, app_secret)


        user_id = data.get(u"user_id")
        url = 'https://graph.facebook.com/%s'%user_id
        fd = urllib2.urlopen(url)
        response = fd.read()
        fd.close()
        userdata = json.loads(response)
        self.write('Hello <a href="%s">%s</a>\n'%(userdata['link'], userdata['name']))




application = tornado.web.Application([
    (r"/", MainHandler),
])


if __name__ == "__main__":
    application.listen(8888)
    tornado.ioloop.IOLoop.instance().start()

The code above seems to be a very big change from our original version. But actually its not that complex.

The improved version uses the POST variables passed by Facebook to our application to enable it to display a more personalized output. For example, Juan dela Cruz, a Facebook user, runs the application, it would display:

Hello Juan dela Cruz

This is possible because when Facebook displays the application, it makes a POST request to the Canvas URL. With the request are POST variables, the important variable is 'signed_request'.


def post(self):
        app_secret = '############' # put your app secret here
        signed_request = self.get_argument('signed_request')
        data = load_signed_request(signed_request, app_secret)


'signed_request' contains signed information about the Facebook user accessing the application. To make it usable, the application decodes its contents using the 'load_signed_request' function. This function is simply a transliteration of the PHP code in their documentation. You could take time to understand it, but for now its enough that it simply decodes the information passed to the application. The contents of 'signed_request' decodes to a JSON string which is returned by 'load_signed_request' as a dictionary object.

{u'user_id': u'11111111', u'algorithm': u'HMAC-SHA256', u'expires': 1309536000, u'oauth_token': u'############', u'user': {u'locale': u'en_US', u'country': u'ph', u'age': {u'min': 21}}, u'issued_at': 1309531299}

From this dictionary object, we use the 'user_id' to get more information about the user using the Facebook Graph API.

        user_id = data.get(u"user_id")
        url = 'https://graph.facebook.com/%s'%user_id
        fd = urllib2.urlopen(url)
        response = fd.read()
        fd.close()
        userdata = json.loads(response)
        self.write('Hello <a href="%s">%s</a>\n'%(userdata['link'], userdata['name']))

We get more information about the user by making a GET request to this URL.

https://graph.facebook.com/[user_id]

The response to this request is a JSON string containing more information about the user. An example of the returned JSON string follows.

{"id":"11111111","name":"Juan dela Cruz","first_name":"Juan","last_name":"dela Cruz","link":"http:\/\/www.facebook.com\/people\/Juandelacruz\/111111111","gender":"male","locale":"en_US"}

From this JSON string, we get the name of the user and other information to make a personalized output.

Ending

The application we discussed is a very simple application. It even does not tackle user authentication, which will allow the application access to more information. However, I believe that it is fitting as a "Hello World" app to give an idea on how a Facebook app works. I'll improve on this app in a later post.