In 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?
- User opens their web browser
- Finds a link to your app – usually your Canvas Page URL (http://apps.facebook.com/[your app name])
- Clicks on the link
- Web browser goes to that site (part of FB) and asks for the webpage
- Facebook sends back the Facebook navbar, with an empty iframe in the centre
- Facebook redirects the browser to your site to get the contents of the iframe – AND adds extra information to the request from the user
- Web browser automatically goes to YOUR site, sending the extra information FB provided it
- Your site creates a webpage, and sends it to the user’s browser
- 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?
- User opens browser
- Clicks on the name of your app
- 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:
- Generate a URL for a custom login page for your app (http://www.facebook.com/login.php?api_key=[your api key goes here] )
- Send a redirect to the user’s web browser to force them to go to that page
- …Facebook will then redirect them back to your “Callback URL” page…
- Read the “auth_token” parameter from the HTTP Query String
- Instantiate an instance of
FacebookRestClient
using your API key and your API secret - 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.
UPDATE 21st Sept 2007: Third part now posted
18 replies on “How to Make Facebook Apps Using Java – part 2”
If you are using a central controller servlet as a dispatcher to your different forms authentication will work throughout your application. Let me know if you guys/girls want me to post an example.
Go ahead, Ajay. Any extra help on getting facebook apps working is greatly appreciated :).
Adam
In its core you still store the authentication in session but you can avoid the code duplication of checking the session for every servlet.
Example:
I am using spring and hence I can define the scope of the bean as session just by the XML configuration. see a good discussion here
http://www.memestorm.com/blog/session-scoped-beans-in-spring-20/
Even If you are not using it could be implemented in similar manner.
I First extend the FacebookRestClient
public class FaceBookClient extends FacebookRestClient {
..
}
then define its mapping
In the constructor do everything to create and authenticate session but no utility call.
Also I use a central servlet for all requests.
This goes in web.xml
app
org.springframework.web.servlet.DispatcherServlet
2
app
*.html
In which I inject through setter depndency injection the class I created above.
and now whenever you want to make a call get the available bean and you are ready to go.
Now its available to my whole application authenticated
If you do use the technique discussed in
http://www.memestorm.com/blog/session-scoped-beans-in-spring-20/
do remember not to make it singleton as you would need one authentication per user. so use only the first technique in the article.
Hello,
I’m hoping someone here could help! I’ve been following the examples listed here (thanks for putting them up – they are great). However I’m running into a problem. Following is the code (which I have cut down from your givn example and hardcoded in the API key for simplicity sake) :
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws IOException{
logger.info(“Requested URL: ” + req.getRequestURL()
+ ” — with query string: ” + req.getQueryString());
String authToken = req.getParameter(“auth_token”);
try{
if(authToken == null || authToken.length()
Adam – Are you in the US ? I’m in the UK and find it very difficult to find web hosting with J2EE support.
Yes, the UK is generally useless for hosting of any kind. Too many people are willing to pander to the greedy and lazy hosting companies that charge twice the going rate for half the service.
My advice: pay in dollars, and you’ll find the service improves *dramatically*. Even when running companies, we’ve nearly always done all hosting in the USA – servers in the US are so much better for the same price that you end up getting similar levels of service despite them being thousands of miles away.
I am having an issue during re-direction because the user is not logged in:
logger.info( “User is not logged in; redirecting them to Facebook’s login page = “+defaultLoginPage );
resp.sendRedirect(defaultLoginPage);
The problem is that rather than logging in seamlessly and going to the canvas, when I select my application from the applications toolbar, Facebook throws an error:
Errors while loading page from application
Runtime errors:
HTML tag not supported: “title”
HTML tag not supported: “meta”
……
What it appears to be doing is attempting to render the login page in the iframe instead of going there to authenticate and returning to render the canvas.
Any ideas what I could be doing wrong?
Sorry I’ve figured this out, I had the application set to FBML rather than iFrame so it was rendering the content
One to watch out for I guess…
Question for Ajay – did you extend the dispatcher servlet to redirect to the facebook login page if they are not logged in or if the session doesn’t have the credentials? Your method makes perfect sense if the restclient can be successfully created and then it is available throughout your app, but I don’t understand how you handle the case where the user needs to login
Very helpful post for those starting with facebook app using java.
One suggestion – here is a better alternative to doing the authentication in a servlet. Since it’s a “cross cutting concern” across the application so it’s best handled by intercepting all the requests using a servlet filter. Have a look at this :
http://theliveweb.net/blog/2007/10/31/facebook-authentication-using-java/
Ajay, I’m using Spring and I’m looking into session-scoped beans, but I do not understand how you did the “In the constructor do everything to create and authenticate session but no utility call.” part : for this, you need access to the request/response objects….. how do you get access to them within your session-scoped bean ?
thanks
The problem is that the redirect happens WITHIN the iFrame, thus getting the FB login form and the FB homepage (after the login) INSIDE the iFrame.
I have the same problem as Joe. I had an app set to FBML. When login is needed, the FB login page shows up in canvas, instead of the main browser page. Thus causing error because the canvas cannot show a full-blown html page. How to correct this? (I have login redirect response.sendRedirect(…)).
This is a very nice article and guide, keep up the good work.
I however, do noticed the overly used J2EE word here which might confuse your readers that you need a J2EE server. A normal java web server would’ve been able to do the job too.
I am trying re-do this example using the JSF framework but am having some difficulty with the
response.sendRedirect(loginUrl)
Facebook seems to be embedding their own page. So instead of redirecting the whole page it only redirects where it is getting my pages content from. Any ideas?
I’m tryng to download the source:
source code for FacebookLoginServlet.java.
But the link doesn’t work…where can I find it?
Thanks
Do you have any idea about why both
String fb_session_key = request.getParameter(“fb_sig_session_key”);
String authToken = request.getParameter(“auth_token”);
return null value?