CS6320:  SW Engineering of Web Based Systems

 

the first part, I covered a very high-level, idiot-guide to getting started with writing a Facebook app in java. This part will cover the details of how to architect your own code for basic Facebook authentication, and include code samples you can use to get up and running more quickly. It will also explain in more detail precisely how Facebook’s servers interact with your code, and what you can expect (and what their servers expect of you!).

NB: if the quoted source code is unreadable because it disappears off the edge of the screen, select it and copy/paste (or view source of the page to see it). The most useful stuff is put together into one class you can download here - source code for FacebookLoginServlet.java.

iFrames vs FBML

Facebook apps can be implemented one of two ways, iframes, or FBML.

iFrame FBML
Standard part of HTML, embeds one webpage inside another Proprietary Facebook programming language, very similar to HTML
You can view pages through a normal web-browser to test them You cannot view anything except by running it live on Facebook
Every Facebook page view goes via your webserver, so you can debug problems All requests are internal on the Facebook servers, you cannot find ANY information if something goes wrong
Requires you to provide your own webserver Can run without an external webserver ***

*** - NB: actually, the way Facebook have designed their system, you need your own webserver anyway, they won’t let you make a Facebook App without one. So, although in theory FBML would be a faster and simpler way to get a basic app up and running, in practice, it doesn’t save you any time or effort at all.

Mostly, I’ll be assuming you want to write your app using iframes instead of FBML. FBML is great, and for some parts of your app you will want to use it, but the fact you can’t debug it makes life extremely difficult. If you can get all your code working with iframes first, then it will be relatively easy to switch some bits to FBML when the time comes. It’s much harder to try things the other way around.

Facebook Apps: Detailed explanation

What happens when a Facebook user tries to use your Facebook App?

  1. User opens their web browser
  2. Finds a link to your app - usually your Canvas Page URL (http://apps.facebook.com/[your app name])
  3. Clicks on the link
  4. Web browser goes to that site (part of FB) and asks for the webpage
  5. Facebook sends back the Facebook navbar, with an empty iframe in the centre
  6. Facebook redirects the browser to your site to get the contents of the iframe - AND adds extra information to the request from the user
  7. Web browser automatically goes to YOUR site, sending the extra information FB provided it
  8. Your site creates a webpage, and sends it to the user’s browser
  9. User’s browser renders your webpage inside the iframe embedded inside the page it got from FB

…but what does this look like to the user?

  1. User opens browser
  2. Clicks on the name of your app
  3. Your app appears on the screen inside the navbars etc for Facebook

So, the first thing to understand, especially if you’ve used FB a lot yourself, is that there’s a lot more going on behind the scenes than appears to the user. This will become especially clear when you make a mistake and start trying to debug your app.

Authentication

There are two “modes” in which you can create a webpage to display as part of your app:

  • Default
  • Authenticated

In the default mode, i.e. if you do nothing special, you have NO ACCESS to any of Facebook’s data. You cannot find out ANYTHING about the Facebook user - you can’t find their name, you can’t find their friends, you can’t post items on their Wall, nothing.

In the authenticated mode, you have full access to:

  • read all the data of the currently logged-in user
  • most of the data of all OTHER users of your app (people who have “added” your app to their Facebook account)
  • send messages, post newsitems, etc, from the logged-in user (but they have to OK this manually)

Authenticating in java

FB provides you with a java class, FacebookRestClient.java, which you can use to authenticate with the FB servers. Once you have authenticated an instance of this class, that class will then ALSO provide you with access to all the features of Facebook (reading data about users, sending messages to other users, etc).

FB has partially documented the way that authentication works, but there are lots of missing details. In particular, there is no documentation on how to use the FacebookRestClient class to do authentication, and several of the methods that appear to work together are actually mutually exclusive. This is because that one class is responsible both for doing Web-based apps (the only kind of app that most people have ever seen or used on Facebook) and Desktop apps (which do exist but are much less common right now).

In brief, you need to:

  1. Generate a URL for a custom login page for your app (http://www.facebook.com/login.php?api_key=[your api key goes here] )
  2. Send a redirect to the user’s web browser to force them to go to that page
  3. …Facebook will then redirect them back to your “Callback URL” page…
  4. Read the “auth_token” parameter from the HTTP Query String
  5. Instantiate an instance of FacebookRestClient using your API key and your API secret
  6. Invoke .auth_getSession( auth_token ) using the value of auth_token you received

Authenticating your servlets

Now, you may notice a problem with the above sequence of events. Traditionally, when writing a J2EE app, you have multiple servlets, one for each “page” of your website - but in order to authenticate, the servlet MUST redirect the user back to Facebook, which will then ALWAYS send the user back to your Callback URL page, which means they will be sent to the “wrong” page.

Note: just because you redirect the user to Facebook’s login page doesn’t actually mean the user SEES a login page - if they are already logged-in to Facebook, FB just automatically sends them straight back to your site (but this time, it gives your site the login information, which doesn’t otherwise do!)

So, effectively, Facebook ONLY ALLOWS ONE SERVLET to authenticate with Facebook - whichever servlet you gave the address of when you filled in the “Callback URL” field when creating your app on Facebook’s Developer page.

That’s not a big problem, though. J2EE servlets have the Session object, automatically maintained by your J2EE container, that will take care of this: if you ever want the user to be authenticated when using your app, just make the Callback URL page force the user to login once (as noted above, if already logged in to FB, the user won’t be asked to login again, but you still have to force FB to make the check), and then store the authenticated instance of FacebookRestClient.java in the J2EE Session object, like this:

protected void doGet(HttpServletRequest request, HttpServletResponse response)
{
   ...
   FacebookRestClient authenticatedClient = new FacebookRestClient( apiKey, secret );
   authenticatedClient.setIsDesktop(false);
   request.getSession().setAttribute( "auth", authenticatedClient );
}


Then, whenever you need to interrogate Facebook about any data in ANY of your servlets, fetch the authenticated FacebookRestClient instance from the J2EE session, and use it directly:

protected void doGet(HttpServletRequest request, HttpServletResponse response)
{
   FacebookRestClient authenticatedClient = (FacebookRestClient) request.getSession().getAttribute( "auth" );
   ...
}


In fact, I’d advise you to dedicate one servlet purely to logging-in to Facebook, and make that servlet your Callback URL one. For instance, here’s source code for FacebookLoginServlet.java you could use directly for that servlet. Note that the provided source code assumes you are using Apache Log4j to do your logging - you may want to change that to use Sun’s java.util.logging API. I advise you at least use some form of logging (it helps a lot when debugging!)

Old sessions

You also have to handle the situation where a player leaves their web browser open for a long time, long enough that the J2EE Session timesout and/or the Facebook authentication timesout, and then they hit “refresh”. If you’re merely starting every doGet / doPost method in your servlets with the request.getSession() call above, you’ll find that either the Session is empty and you get a null client, or you get a client but that when you attempt to invoke methods on it they all fail with FacebookException’s. Exactly what will go wrong depends upon the implementation of your J2EE container, and how long it keeps Sessions alive for before expiring them (and whether the connected web browser kept all the Cookie’s it was given).

For this, I currently just surround all my doGet’s with a big try/catch:

protected void doGet(HttpServletRequest request, HttpServletResponse response)
{
   try
   {
      ...
   }
   catch( FacebookException e )
   {
      logger.error( "Facebook exception, probably due to timeout on authentication; sending error page to user", e );
      String loginPage = "http://www.facebook.com/login.php?api_key="+apiKey+"&v=1.0";
      response.getWriter().println( "<h1>Error: Couldn't talk to Facebook</h1>" );
      response.getWriter().println( "Your Facebook session has probably timed out" );
      response.getWriter().println( "Please <a ref=\""+loginPage+"\">click here</a> to login again");
   }
}

Testing…

And, again, time to finish off with some basic tests, so you can be sure all this stuff above is working. For my basic testing, I created a servlet that would fetch the user-id of the currently-logged-in Facebook user, and ask Facebook for a list of all their friends’ names. This is the same basic test that Facebook uses in their one and only piece of sample java code in their API.

public class TestFaceBookServlet extends HttpServlet
{
   protected void doGet( HttpServletRequest request, HttpServletResponse response ) throws IOException, FacebookException
   {
      FacebookRestClient client = (FacebookRestClient) request.getSession().getAttribute("facebookClient");
      client.setDebug(true);

      logger.info( "Fetching friends for user to prove that our auth to FB's server worked OK ... should see a list of \"uid\"s now, with NO java-exceptions!");
      Document d = client.friends_get();

      FacebookRestClient.printDom(d, "  ");
   }
}

Conclusion

By now you should have:

  • A facebook app that you can connect to via Facebook itself that displays the content from your basic servlets
  • A login-servlet as your Callback URL that automatically logs in the current user to Facebook
  • A simple test servlet that your login-servlet redirects to after a successful login, and which prints in your server log-files a list of all the uid’s of your friends

In the next part, I’ll probably stop to cover likely problems and gotchas you’ll run into, since there’s quite a few you’re likely to hit by this point.

If there’s anything else you’d like to see covered, add a comment at the bottom and I’ll try to fit it in as well.

© Lynne Grewe