Facebook Access Tokens from Canvas Apps
UPDATE 26/07/10: looks like the Facebook Platform team is addressing this issue presently. There's a new migration option available to send session data in the request. When I used this parameter it seems that all old session related fb_sig parameters were no longer being sent, so I'd probably avoid using this. Instead refactor your applications to use the new OAuth support for Canvas apps, which is also available to enable in the App Settings Migration tab.
There's an open bug over at the Facebook bug tracker detailing an issue people are having with Canvas pages and the new Graph API. Right now, Facebook canvas applications are posting a session_key to the canvas callback URL. The new OAuth system of course uses the access token system, rather than the session_keys from the old Rest API.
There's a bit of info hiding in the documentation upgrade guide that outlines how to "exchange" a session key for an access token. All you need to do is POST some info to an endpoint and it'll spit back a valid access token.
Like so:
$ch = curl_init("https://graph.facebook.com/oauth/exchange_sessions");
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, array(
"type" => "client_cred",
"client_id" => "<your canvas application id>",
"client_secret" => "<your canvas application secret>",
"sessions" => $_REQUEST["fb_sig_session_key"]
));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$result = json_decode(curl_exec($ch));
$accessToken = $result[0]->access_token;
So what's going on here? You're POSTing a request to the Facebook OAuth implementation to exchange your session key for an access token. You provide your application id and secret (important: this needs to be id and secret for the canvas application the request originated from of course) and the session ID of the user. Send this off and a JSON response will come back with the access token. Also, if needed, there's another property passed back - "expires", which will let you know when this access token will die.
Okay, so now you have an access token for use with the new API. Now what?
There's a little bit of fudging required to use this with the PHP SDK. The setSession() method of the PHP SDK doesn't just take an access token, it expects all the other crap that is usually contained in the cookie it saves. The idea behind my solution is that you shouldn't need to modify any base classes. The code below will con the Facebook library into accepting your newly acquired access token:
$session = array(
"uid" => "",
"session_key" => "",
"secret" => "",
"access_token" => $accessToken
);
ksort($session);
$sessionStr = "";
foreach($session as $sessionKey => $sessionValue) $sessionStr .= implode("=", array($sessionKey, $sessionValue));
$session["sig"] = md5($sessionStr."<your app secret>");
$facebook->setSession($session, false);
What this code is doing is constructing a fake session object. Since the only thing the SDK actually looks at in this session data is the access_token, we just fill the other entries with an empty str. Once this is done, we just calculate a signature for the data, as per the Facebook Wiki documentation. Again, we're only doing this because the setSession() method demands it.
Once this is done, you can go ahead and make api calls! Now wasn't that easy?
Also, as a little footnote, you should know that you don't actually have to fudge the session, you can just pass the access token you get into each api call, like so:
$facebook->api(array("method" => "stream.get", "access_token" => $accessToken));
... But this is a bit cumbersome. Also, with the fake session, you can just remove my code later when Facebook starts sending access token in the initial POST data.
You can see this solution in action at this Facebook page. Just click grant access, then click Go.
Hope this helps someone!

June 1st, 2010 - 00:18
I have followed your instructions.
My code for token upgrade (as an additional methods for php-sdk 2.0.4)
public function upgradeToOAuth2 ($install_oauth2_token = false)
{
$session = $this->getSession();
if ($session != null)
{
$result = $this->_graph(‘/oauth/exchange_sessions’,'GET’,
array(
‘client_id’ =>$this->getAppId(),
‘client_secret’ =>$this->getApiSecret(),
‘sessions’ =>$session['session_key'],
‘type’ => ‘client_cred’
));
if ($install_oauth2_token && is_array($result))
{
if (isset($result['access_token']))
$this->setOAuth2AccessTokenToSession($result['access_token']);
}
return $result;
}
else
return null;
}
private function setOAuth2AccessTokenToSession($access_token)
{
$session = array(
“uid” => “”,
“session_key” => “”,
“secret” => “”,
“access_token” => $access_token
);
ksort($session);
$sessionStr = “”;
foreach($session as $sessionKey => $sessionValue)
$sessionStr .= implode(“=”, array($sessionKey, $sessionValue));
$session["sig"] = md5($sessionStr.$this->getApiSecret());
$this->setSession($session, false);
}
And I still can’t get it to work.
Here is the API calls:
$at = $fb->upgradeToOAuth2(true);
// proper access token response from FB
$isUser = $fb->api(array(‘method’=>’users.isAppUser’,
‘uid’=>$me['id']));
Here I get the exception:
Exception: 190: Invalid OAuth 2.0 Access Token
Please advice. Thank you in advance.
June 1st, 2010 - 11:07
Hello Andrei,
I think you just need to test this code a little more. From what I can see, you’re now using $this->_graph to connect to the exchange_sessions endpoint… But you’re using GET. The documentation says specifically to use POST, so you might not be getting a valid response back. I think you just need to see what kind of response you’re getting back from your exchange_sessions code.
Also, if you’re going to wire this functionality into the Facebook PHP SDK, you might as well use the built in signature generation functionality. So instead of
ksort($session);
$sessionStr = “”;
foreach($session as $sessionKey => $sessionValue)
$sessionStr .= implode(“=”, array($sessionKey, $sessionValue));
$session["sig"] = md5($sessionStr.$this->getApiSecret());
Just use:
$session["sig"] = $this->generateSignature($session);
Also, I’m not sure why you’re checking for the presence of a session in upgradeToOAuth2()? The point of this trick is that the PHP SDK currently won’t automatically set up a session in response to a POST request from a Facebook Application Profile Tab … So session should be null already.
June 14th, 2010 - 14:42
Hi.. one doudt I see in the example fanpage you linked that you are getting the the fanpage ID ([fb_sig_profile] => XXXXXXXXXXXX) in using the new SDK.
How did you manage to get that… I have done everything and nothing… I many people in the FB forums are looking for that also… Any solution or code?
I do can get it using the old SDK
June 14th, 2010 - 15:23
With the new SDK I just get something like
session: {\”uid\”:\”XXXXXXXXXXXX\”,\”session_key\”:\”XXXXXXXXXXXX\”,\”secret\”:\”XXXXXXXXXXXX\”,\”expires\”:1276498800,\”access_token\”:\”XXXXXXXXXXXX\”,\”sig\”:\”XXXXXXXXXXXX\”}
June 15th, 2010 - 09:06
Engel, using the new or old SDK won’t dictate what POST variables are sent by Facebook. One thing I have noticed though is that even when a Facebook visitor has granted your app access, page requests won’t send things like fb_sig_profile unless it’s via a FBJS Ajax request.
June 16th, 2010 - 12:10
Using the exchange_sessions to get the access_token worked within my Tab Page (FBML). However, I can’t seem to make API calls using the PHP SDK.
code:
try {
$request = “/{$_REQUEST['fb_sig_user']}/feed?access_token=” . $token[0]->{‘access_token’};
$graph = array(‘access_token’=>$token[0]->{‘access_token’}, ‘message’=>$message);
$facebook->api($request, ‘POST’, $graph);
} catch (Exception $e) {
echo $e;
}
outputs:
Exception: (#200) The user hasn’t authorized the application to perform this action.
Has anyone been able to get extended permissions from within an FBML Tab Page?
Thanks.
June 16th, 2010 - 12:12
JP – have you actually prompted the user to grant your application access from the tab page itself? There’s a couple of different ways to do it, either using FBML markup buttons or using FBJS.
June 16th, 2010 - 12:45
I’ve been trying to use:
Grant extended permissions
This should open an overlay with the Allow/Don’t Allow options. But this doesn’t ALWAYS happen. I’m not sure if it’s a Facebook bug, but my code is pretty lean, so I think it is a problem on the FB side. When it does fail I get this in my Firebug console:
PlatformCanvasController.singleton is undefined
June 16th, 2010 - 12:45
Grant extended permissions
June 16th, 2010 - 12:46
Sorry, it’s a link with this:
onclick=”Facebook.showPermissionDialog(‘publish_stream’);return false;”
July 1st, 2010 - 03:30
Thank you so much, your tips save my day..
July 3rd, 2010 - 06:56
Is this still working? What is the Access Token tab supposed to do. It gets stuck at a loading state for me.
July 7th, 2010 - 12:22
THANK YOU!
I spent nearly 3 hours trying to search right solution – this should not even be problem – getting session/making simple API call from PHP SDK should not be so difficult.
Anyways, thank you – saved me so much trouble – I wish I had gotten to your blog 2 hours before
July 13th, 2010 - 16:34
Sam, I’m seeing similar results to Engel when getting the iframe’s query string directly.
{“uid”:”*****”,”session_key”:”*****”,”secret”:”***”,”expires”:1279008000,”access_token”:”*****”,”sig”:”****”}
This is not what I was getting…maybe last week…
July 14th, 2010 - 12:22
Perhaps Facebook has finally updated the POST data they are sending out in Canvas requests. I’ll do some investigation later and put up an update if this is the case.
July 23rd, 2010 - 16:49
Hi all,
Edit your application settings
Migrations-> Enable Canvas “Session Parameter”.
Which will add a parameter “session”, a json object contains(uid, access_token,
session_key, expires …) in your Canvas call back URL
July 26th, 2010 - 12:34
Sreekanth,
I’ve taken a look at this new migration option, it seems enabling it or the new OAuth signed_request will completely change the way Facebook sends POST data to endpoints, no more fb_sig_* parameters will be sent.
I’ll update my post to make people aware of it however.
August 4th, 2010 - 00:11
Hi
Your post is great! Am trying to do this, hope you can advise. I did a application to pull specific wall posts from a facebook page. the application can be added to any facebook page to grab the wall posts by a search keyword, or search wallposts functionality. Been trying to get the access token but to no avail. it just won’t return any data at ll
August 11th, 2010 - 04:55
Or you could save yourself a ton of time and energy and do what Sreekanth P said a few comments ago. It will fix your problem without the need for a single line of code.
September 2nd, 2010 - 01:14
great! just what I was looking!
November 24th, 2010 - 07:45
This post is an absolute godsend to confused developers in Facebook world (like me). Thanks, Sambro!
December 7th, 2010 - 17:06
Hi,
The OP’s post works great even though i have no idea how it works. I only added my application secret not even my application id and it enables old rest api setCount. Thank you.
February 2nd, 2011 - 22:25
The problem right now with using OAuth 2.0 (so we get signed_request), or enabling the ‘session’ key is that we then ONLY get those variables through in POST. The rest (fb_sig_*) variables are lost, for me at least. Right now, that means there’s no way to check in a fan page tab if the viewing user is a fan without first getting the user to authorise. With legacy canvas authentication, we had fb_sig_is_fan, currently there’s no direct alternative with signed_request. The documentation suggests checking the user’s likes via Graph. That’s fine if the user has authorised so we have their user ID, but we don’t on fan page tabs, unless we’re doing an Ajax call.
May 4th, 2011 - 14:02
Mate,
you are an absolute genius! I am gonna worship you! LOL
This is exactly what I needed. I have been looking for this for about three days now! The part were you “self-manufacture” the session… brilliant!
I don’t know what I would have done without this. Thank you so much for sharing this!
You are the man!! (Not kidding) I hope someone pays you shit loads of money for the work you do. You certainly deserve it! haha.
I am gonna drink on you!
Thanks again!
May 17th, 2011 - 19:44
Awesome, I was looking for this very solution. Code works wonderful. Thank you!