views:

7377

answers:

1

I've got a Facebook iframe application that is completely external. By this I mean that once a user accesses the canvas URL to load the application, all the links in the iframe app go to my servers, and the canvas page never gets refreshed unless the user navigates to somewhere else on Facebook and comes back (or does a browser refresh).

On the initial load of the app where Facebook creates the iframe, I get passed all the usual parameters like fb_sig_user which allows me to create an internal app session based on the facebook user. This app session (which is not the Facebook session, it's my own app session) is all I need to allow the user to work with the app.

The problem comes an hour later. If the user leaves the computer, or uses the app for more than an hour, the Facebook session expires. There are some app pages which require fetching friend information, and once the FB session has expired, these pages break, throwing out errors such as "Error: Session key invalid or no longer valid".

My question is whether there is a way to refresh the user's Facebook session from within an iframe application to keep it from expiring an hour later. Do any of the API calls do this? Is there a Facebook Connect trick to ping something? Is there any definitive method to keep it alive? I haven't been able to find any examples that specifically address this.

+13  A: 

Victory is mine!

There is an almost wholly undocumented Facebook feature dealing with iframe sessions, that I found a vague reference to in my research. This page doesn't really explain it well however, and only after several hours of watching various session keys in my iframe was I able to figure out what was going on.

Previously, my iframe app was receiving the usual round of fb_whatever parameters when the initial iframe load occurred. So in my application, I was doing this on every request:

if (isset($_REQUEST['fb_sig_session_key'])) {
 $_SESSION['fb_sig_session_key'] = $_REQUEST['fb_sig_session_key'];
}
if (! empty($_SESSION['fb_sig_session_key'])) $this->facebook->api_client->session_key = $_SESSION['fb_sig_session_key'];

This code would receive the fb_sig_session_key on the initial app load, and I would squirrel it away into a local $_SESSION for use with the API. Storing it in the local session is necessary, because fb_sig_session_key never gets passed in again unless you reload the entire app iframe.

So the problems occurred when this session key expired an hour or so later.

After looking at the vague reference page, I started examining all the $_REQUEST variables I was getting. It turns out that even on an internal link inside your iframe app, Facebook modifies the request to pass along some parameters. For some reason, they have an entirely different, but also valid session key that comes along with every iframe request!

This parameter is named after your Facebook Application api key. So if your application API key is "xyz123", every request inside your iframe gets a parameter called xyz123_session_key (as well as a few others, like xyz123_expires and xyz123_user).

After watching the associated expiry time for the main session (the original fb_sig_session_key) and this iframe-only session (xyz123_session_key), the light at the end of the tunnel appeared: the iframe-only session key expiry time actually gets updated occasionally. I haven't determined when or how (I assume it's an Ajax ping at some point), but nonetheless, it refreshes.

I waited for the original fb_sig_session_key session to expire, and sure enough the friend-related pages in my app started coughing up errors. At that point, I switched my locally-stored session key to the new iframe-only xyz123_session_key, and the problem was solved. That session works just as well as the original!

So, my final code fix is to store the session key locally as follows:

$iframeSessionKeyName = $CONFIG['facebook']['apiKey'] . '_session_key';
if (isset($_REQUEST[$iframeSessionKeyName])) {
    $_SESSION['fb_sig_session_key'] = $_REQUEST[$iframeSessionKeyName];
}
else if (isset($_REQUEST['fb_sig_session_key'])) {
    $_SESSION['fb_sig_session_key'] = $_REQUEST['fb_sig_session_key'];
}
if (! empty($_SESSION['fb_sig_session_key'])) $this->facebook->api_client->session_key = $_SESSION['fb_sig_session_key'];

This gives preference to the "iframe-only" key.

Edit: My original assumption that the "iframe-only" key was updated via some kind of Ajax method was wrong, it turns out these values are set into a cookie by Facebook. This leads to some cross-domain problems when using these cookies. Setting a P3P cookie policy will alleviate this with most browsers, except Safari. There is still no good work around for Safari.

zombat
It looks like this method utilizes cookies, so Safari has some difficulty with it, as the cookies are cross-domain.
zombat