Facebook IFrame App in Safari

You don't really need cookies to make your Facebook IFrame app work in Safari browser. I remember two years ago, to make Page Tab applications work in Safari, you only need to make use of this trick. I won't be explaining it in details here.

Just recently, I was asked to do this new Fb project that's intended for Page Tab, which now is an iFrame-only platform. I've been developing it using Chrome browser. And when I finally get to test it on Safari, unsurprisingly, it's the same old scenario. What's worse is that the old trick won't work anymore (you better read this).

This app relies so much on Javascript. And instead of deploying several php pages, I only need to come up with a startup page which when loaded on the iFrame, loads the rest of the required scripts. Other pages are dynamically constructed using html fragments that are parsed by a templating script. So most of the Client-Server transactions are done asynchronously. And with Safari continously blocking third-party cookies, it just doesn't work.

After hours of googling for answers, I turned to Facebook's PHP SDK. I found out that it doesn't really require you to use cookies after all (now at 3.1.1). If you take a look at `BaseFacebook` class, particularly the `getSignedRequest` method, you'll see that it first checks the request parameter for Signed Request data (they used to have `session` before this).

...
public function getSignedRequest() {
  if (!$this->signedRequest) {
    if (isset($_REQUEST['signed_request'])) {
      $this->signedRequest = $this->parseSignedRequest(
        $_REQUEST['signed_request']);
    } else if (isset($_COOKIE[$this->getSignedRequestCookieName()])) {
      $this->signedRequest = $this->parseSignedRequest(
        $_COOKIE[$this->getSignedRequestCookieName()]);
    }
  }
  return $this->signedRequest;
}
...

With this in mind, it's possible to send `signed_request` data along with an Ajax request which the PHP SDK can use to determine the user and for your app to verify as well. I use FB.getLoginStatus to acquire a copy of this data. This of course assumes that the user has already authorized your app. Otherwise you'll have to let him do so.

The following example uses jQuery's `$.ajax`.

...
$.ajax({
  url: BACKEND_LINK_HERE,
  type: 'POST',
  data: {
    signed_request: 'sOm3ReA||yLo0ooooooo...oooooooooNgT3xtHeR3',
    another_data: 12345
  }
})
...

So that's it. You need to make sure you have a valid `signed_request` data each time a user interacts with your application. Don't even think about storing it because the access token contained will soon expire which then deems it useless (about 2.5 hours from the moment the user interacted with your app). Yes that's right. And just to let you know, `offline_access` is no longer supported.

Here's a Facebook article about handling invalid and expired access tokens in case you're interested.


Related posts:

3 comments :

Anonymous said...

This helped me fixed my Safari issue. Thanks!

Anonymous said...

I don't really understand the example. What is the backend link, and how exactly would you get the signed_request using FB.getLoginStatus?

bassgrinder said...

Backend link is the URL to your backend or server script such as PHP (e.g: https://my.domain.com/site/register.php).

To get the `signed_request` value using FB Javascript SDK, first, you'll need to make sure your app is authorized by the user. It's easy to check for that by using `FB.getLoginStatus()`. You better go check its documentation first (https://developers.facebook.com/docs/reference/javascript/FB.getLoginStatus/). The first argument of this function is a callback function which accepts a `response` object. It contains the ff:

{
status: 'connected',
authResponse: {
accessToken: '...',
expiresIn:'...',
signedRequest:'...',
userID:'...'
}
}

Check if the value of `response.status` matches to `connected`. If so, then you also have the `signed_request` value through `response.authResponse.signedRequest`.

Post a Comment

Hi there! Please leave your message here. Also, I may not be able to respond to your query right away. So please bear with me. Thanks. :)