title: How to handle Facebook's signed_request for iFrame Canvas applications?
body: <p>I'm developing an iFrame Canvas application for Facebook using CakePHP, its Auth component, WebTechNick's <a href="http://www.webtechnick.com/wtn/blogs/view/238/Facebook_Plugin_v2_0_Graph_System" rel="nofollow">Facebook plugin</a> and OAuth for canvas pages (I've enabled this in the Facebook Developer app options). I would like users to be able to use the application after adding it to their profile (by requesting <code>email</code> and <code>publish_stream</code> permissions) by visiting <a href="http://apps.facebook.com/myapp/" rel="nofollow">http://apps.facebook.com/myapp/</a> or as a tab in their profile.</p>
  
  <p>Requesting permissions is not the problem. The user is redirected to the permissions request page and then redirected to a callback method which requests an <code>access_token</code>, as per this <a href="http://kartiklad.com/oauth-2-0-and-graph-api-for-facebook-canvas-applications/" rel="nofollow">tutorial</a>. </p>
  
  <p>After this callback the user is redirected back to <a href="http://apps.facebook.com/myapp/" rel="nofollow">http://apps.facebook.com/myapp/</a> which shows their personal index page. This is also where the problems start. As soon as the aforementioned URI is loaded, the browser asks for a form resubmission, this happens every time I reload <a href="http://apps.facebook.com/myapp/" rel="nofollow">http://apps.facebook.com/myapp/</a>. This is the case because Facebook wants to pass the (expected) <code>signed_request</code> parameter and I'm wondering what to do with it. It's not an empty variable, so do I need another validation method or redirect, perhaps?</p>
  
  <p>How should I handle the procedure for the <code>signed_request</code> parameter and, more importantly how to get rid of this form resubmission dialog?</p>
  
  <p>Some of my methods, they might be a bit of a mess due to all the experimentation of the past day.</p>
  
  <p><code>beforeFilter</code>, <code>login</code> and <code>callback</code> methods, in my UserController.php:</p>
  
  <pre><code>  function beforeFilter() {
      parent::beforeFilter();
      if (empty($this-&gt;permissions)) {
        $this-&gt;Auth-&gt;allow('login', 'logout', 'callback');
      }
    }
  
    function login() {
      $session = $this-&gt;facebook-&gt;getSession();
      $login_url = 'https://graph.facebook.com/oauth/authorize?client_id=' . FACEBOOK_APP_ID . '&amp;redirect_uri=' . MY_APP_URL . '/users/callback/&amp;type=user_agent&amp;&amp;display=page&amp;scope=' . FACEBOOK_APP_PERMISSIONS;
      if($session){
        try {
          $uid = $facebook_client-&gt;getUser();
          $me  = $facebook_client-&gt;api('/me', $params);
          print($me);
        } catch (FacebookApiException $e) {
          error_log($e);
        }
          } else {
        $this-&gt;set('authorise', true);
        $script = '$(document).ready(function() { facebookRequestPermissions("'.$login_url.'");});';
        $this-&gt;set('script', $script);
          }
    }
  
    function callback() {
      function callFb($url, $params) {
          $ch = curl_init();
          curl_setopt_array($ch, array(
              CURLOPT_URL =&gt; $url,
              CURLOPT_POSTFIELDS =&gt; http_build_query($params),
              CURLOPT_RETURNTRANSFER =&gt; true,
              CURLOPT_VERBOSE =&gt; true
          ));
          $result = curl_exec($ch);
          curl_close($ch);
          return $result;
      }
  
      $params=array('client_id'=&gt;FACEBOOK_APP_ID, 'type'=&gt;'client_cred', 'client_secret'=&gt;FACEBOOK_APP_SECRET);
      $url = "https://graph.facebook.com/oauth/access_token";
      $access_token = callFb($url, $params);
      $access_token = substr($access_token, strpos($access_token, "=")+1, strlen($access_token));
  
      if ($access_token) {
        $this-&gt;redirect(FACEBOOK_APP_URL);
      } else {
        echo 'An error has occurred';
      }
    }
  </code></pre>
  
  <p>The JavaScript in the login method refers to this jQuery function, the Facebook JavaScript SDK is initialised in <code>$(document).ready()</code>:</p>
  
  <pre><code>function facebookRequestPermissions(login_url) {
    FB.getLoginStatus(function(response) {
      if (response.status !== 'unknown') {
        top.location.href=login_url;
      }
    });
  }
  </code></pre>
  
  <p>The JavaScript function should only fire when a user is logged in, if not, a different landing page is shown.</p>
  
  <p>I use some methods in an overall AppControler:</p>
  
  <pre><code>class AppController extends Controller {
    var $components = array('RequestHandler', 'Session', 'Auth', 'Facebook.Connect');
    var $helpers = array('Form', 'Time','Html','Javascript', 'Session', 'Facebook.Facebook');
    protected $facebook;
    protected $permissions;
    private $user;
  
    function beforeRender() {
      //Save the username if it isn't already present
      if ((int)$this-&gt;Auth-&gt;user('id') != '' &amp;&amp; (string)$this-&gt;Auth-&gt;user('username') == '') {
        $data = array('id' =&gt; (int)$this-&gt;Auth-&gt;user('id'), 'username' =&gt; (string)$this-&gt;user['username']);
        $this-&gt;loadModel('User');
        $this-&gt;User-&gt;save($data);
      }
      if (!empty($this-&gt;user) &amp;&amp; !empty($this-&gt;permissions)) {
        $this-&gt;set('currentUser', $this-&gt;Auth-&gt;user());
      }
    }
  
    function beforeFilter() {
      $this-&gt;Auth-&gt;autoRedirect = false;
      $this-&gt;Auth-&gt;loginAction = array('controller' =&gt; 'users', 'action' =&gt; 'login');
      $this-&gt;Auth-&gt;loginRedirect = array('controller' =&gt; 'users', 'action' =&gt; 'index');
      $this-&gt;Auth-&gt;logoutRedirect = array('controller' =&gt; 'users', 'action' =&gt; 'login');
      App::import('Lib', 'Facebook.FB');
      $this-&gt;facebook = new FB();
      $this-&gt;user = $this-&gt;facebook-&gt;api('/me');
      $this-&gt;permissions = $this-&gt;facebook-&gt;api('/me/permissions');
    }
  }
  </code></pre>
  
  <p><strong>EDIT:</strong>
  This only seems an issue with Firefox. Chrome doesn't display the dialog, but instead does a silent refresh after which the <code>signed_request</code> parameter is empty, strangely enough. This isn't the case with Firefox, where the <code>signed_request</code> parameter remains the same after every prompted refresh (unless the iFrame content is cached), which is looping infinitely, it seems.</p>
  
  <p><strong>EDIT 2:</strong>
  Still struggling with this, but I ended up disabling the <em>OAuth 2.0 for Canvas</em> option in the Facebook Developer application, which has resolved the form resubmission issue. Of course this is not a real solution, because OAuth 2.0 is becoming mandatory for canvas application on Facebook, I believe.</p>