Facebook PHP / JS SDK "Session expired at unix time" with a long token

I am trying to use a stored access token, but after 2 hours I get the error below from the chart API. I wrote a code to send to a user on Facebook in order to get a new code that can be exchanged for an access token that works just fine, except that this happens with every subsequent page request. Facebook continues to strip my access token with an error below, even though the access token is being returned by their server.

Error validating access token: Session has expired at unix time 1338300000. The current unix time is 1338369365. 

Below is an example of a complete test page. Omitting keys for obvious reasons. Click on the page, log in, and then leave it for a few hours and click on the page again (you will get a redirect to facebook and back with the code in the URL), reload the page and it will continue to redirect to facebook and back, an event although I tell him to use the access token that he just returned for the specified code.

 <?php require 'facebook.php'; $app_id = APP_ID; $app_secret = APP_SERCRET; $my_url = URL; $facebook = new Facebook(array( 'appId' => $app_id, 'secret' => $app_secret )); // known valid access token stored in a database $access_token = isset($_COOKIE["FB_LONG_AC_TOKEN"]) ? $_COOKIE["FB_LONG_AC_TOKEN"] : false; $code = $_REQUEST["code"]; // If we get a code, it means that we have re-authed the user //and can get a valid access_token. if (isset($code)) { $token_url="https://graph.facebook.com/oauth/access_token?client_id=" . $app_id . "&redirect_uri=" . urlencode($my_url) . "&client_secret=" . $app_secret . "&code=" . $code . "&display=popup"; $response = file_get_contents($token_url); $params = null; parse_str($response, $params); $access_token = $params['access_token']; } // Attempt to query the graph: $graph_url = "https://graph.facebook.com/me?" . "access_token=" . $access_token; $response = curl_get_file_contents($graph_url); $decoded_response = json_decode($response); //Check for errors if ($decoded_response->error) { // check to see if this is an oAuth error: if ($decoded_response->error->type== "OAuthException") { // Retrieving a valid access token. $dialog_url= "https://www.facebook.com/dialog/oauth?" . "client_id=" . $app_id . "&redirect_uri=" . urlencode($my_url); echo("<script> top.location.href='" . $dialog_url . "'</script>"); } else { echo "other error has happened"; } } else { // success echo("Success: ".$decoded_response->name."<br />"); echo($access_token."<br />"); // Attempt to convert access token to longlife token if we don't have one stored. if (!isset($_COOKIE["FB_LONG_AC_TOKEN"])) { // don't have long life token, so let get one. $ch = curl_init("https://graph.facebook.com/oauth/access_token?client_id=".$app_id."&client_secret=".$app_secret."&grant_type=fb_exchange_token&fb_exchange_token=".$access_token); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 5); curl_setopt($ch, CURLOPT_TIMEOUT, 10); $data = curl_exec($ch); curl_close($ch); $params = null; parse_str($data, $params); if (isset($params["access_token"])) { $access_token = $params["access_token"]; echo("Got long life token.<br />"); setcookie("FB_LONG_AC_TOKEN", $access_token, time() + (3600 * 24 * 60), "/"); } } else { echo("Have long life token already.<br />"); } } if ($access_token) { $facebook->setAccessToken($access_token); // See if there is a user from a cookie $user = $facebook->getUser(); if ($user) { try { // Proceed knowing you have a logged in user who authenticated. $user_profile = $facebook->api('/me'); } catch (FacebookApiException $e) { echo '<pre>'.htmlspecialchars(print_r($e, true)).'</pre>'; $user = null; } } } // note this wrapper function exists in order to circumvent PHP's //strict obeying of HTTP error codes. In this case, Facebook //returns error code 400 which PHP obeys and wipes out //the response. function curl_get_file_contents($URL) { $c = curl_init(); curl_setopt($c, CURLOPT_RETURNTRANSFER, 1); curl_setopt($c, CURLOPT_URL, $URL); $contents = curl_exec($c); $err = curl_getinfo($c,CURLINFO_HTTP_CODE); curl_close($c); if ($contents) return $contents; else return FALSE; } ?> <!doctype html> <html xmlns:fb="http://www.facebook.com/2008/fbml"> <head> <title>Facebook Auth</title> </head> <body> <?php if ($user) { ?> Your user profile is <pre> <?php print htmlspecialchars(print_r($user_profile, true)) ?> </pre> <?php } else { ?> <fb:login-button></fb:login-button> <?php } ?> <div id="fb-root"></div> <script> window.fbAsyncInit = function () { FB.init({ appId: <?php echo($app_id); ?>, cookie: true, // enable cookies to allow the server to access the session oauth: true, // enable OAuth 2.0 xfbml: true // parse XFBML }); FB.getLoginStatus(function (res) { console.log(res.status); }); }; (function(d){ var js, id = 'facebook-jssdk'; if (d.getElementById(id)) {return;} js = d.createElement('script'); js.id = id; js.async = true; js.src = "//connect.facebook.net/en_US/all.js"; d.getElementsByTagName('head')[0].appendChild(js); }(document)); </script> </body> </html> 

What am I doing wrong? or is it a problem with facebook?

UPDATE:

I updated my code to follow the stream sent by @cpilko, but I still have the same problem. I can get in and out just fine. However, if after a few hours I go to the test page, I will say that the next day I will receive an expired exception with the provided access token for a long service life (FB JS SDK considers that I am connected, but the server did not do this), I I refresh the page and I show that both the server and the FB JS SDK are logged in, and the long token that I get from facebook matches the one I used in the original (saved in my cookies). I do not understand why I can not use a long-term token for the first time. Updated code below.

 <?php require 'facebook.php'; $app_id = "XXXXXXXXXXXXX"; $app_secret = "XXXXXXXXXXXXXXXXXXXX"; $my_url = "http://swan.magicseaweed.local/facebook/"; $facebook = new Facebook(array( 'appId' => $app_id, 'secret' => $app_secret )); $valid_user = false; var_dump($_COOKIE); echo("<br />"); if (isset($_COOKIE["FB_LONG_AC_TOKEN"])) { // Have long term token, attempt to validate. // Attempt to query the graph: $graph_url = "https://graph.facebook.com/me?" . "access_token=" . $_COOKIE["FB_LONG_AC_TOKEN"]; $response = curl_get_file_contents($graph_url); $decoded_response = json_decode($response); // If we don't have an error then it valid. if (!$decoded_response->error) { $valid_user = true; $access_token = $_COOKIE["FB_LONG_AC_TOKEN"]; echo("Have long life token.<br />"); } else { // Stored token is invalid. // Attempt to renew token. // Exchange short term token for long term. $ch = curl_init("https://graph.facebook.com/oauth/access_token?client_id=".$app_id."&client_secret=".$app_secret."&grant_type=fb_exchange_token&fb_exchange_token=".$facebook->getAccessToken()); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 5); curl_setopt($ch, CURLOPT_TIMEOUT, 10); $data = curl_exec($ch); curl_close($ch); $params = null; parse_str($data, $params); if (isset($params["access_token"])) { $access_token = $params["access_token"]; echo("Got long life token.<br />"); setcookie("FB_LONG_AC_TOKEN", $access_token, time() + (3600 * 24 * 60), "/"); } else { // Clear invalid token. setcookie("FB_LONG_AC_TOKEN", "false", time() - 3600, "/"); echo("Long life token invalid.<br />"); } } } else if ($facebook->getUser()) { // Have short term access token. // Verify short term token is valid still. try { // Proceed knowing you have a logged in user who authenticated. $user_profile = $facebook->api('/me'); } catch (FacebookApiException $e) { } if (is_array($user_profile)) { // Have user. $valid_user = true; // Exchange short term token for long term. $ch = curl_init("https://graph.facebook.com/oauth/access_token?client_id=".$app_id."&client_secret=".$app_secret."&grant_type=fb_exchange_token&fb_exchange_token=".$facebook->getAccessToken()); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 5); curl_setopt($ch, CURLOPT_TIMEOUT, 10); $data = curl_exec($ch); curl_close($ch); $params = null; parse_str($data, $params); if (isset($params["access_token"])) { $access_token = $params["access_token"]; echo("Got long life token.<br />"); setcookie("FB_LONG_AC_TOKEN", $access_token, time() + (3600 * 24 * 60), "/"); } } } if ($access_token) { $facebook->setAccessToken($access_token); // See if there is a user from a cookie $user = $facebook->getUser(); if ($user) { try { // Proceed knowing you have a logged in user who authenticated. $user_profile = $facebook->api('/me'); } catch (FacebookApiException $e) { echo '<pre>'.htmlspecialchars(print_r($e, true)).'</pre>'; $user = null; } } } // note this wrapper function exists in order to circumvent PHP's //strict obeying of HTTP error codes. In this case, Facebook //returns error code 400 which PHP obeys and wipes out //the response. function curl_get_file_contents($URL) { $c = curl_init(); curl_setopt($c, CURLOPT_RETURNTRANSFER, 1); curl_setopt($c, CURLOPT_URL, $URL); $contents = curl_exec($c); $err = curl_getinfo($c,CURLINFO_HTTP_CODE); curl_close($c); if ($contents) return $contents; else return FALSE; } ?> <!doctype html> <html xmlns:fb="http://www.facebook.com/2008/fbml"> <head> <title>Facebook Auth</title> </head> <body> <?php if ($user) { ?> Your user profile is <pre> <?php print htmlspecialchars(print_r($user_profile, true)) ?> </pre> <?php } else { ?> <fb:login-button></fb:login-button> <?php } ?> <div id="fb-root"></div> <script> window.fbAsyncInit = function () { FB.init({ appId: <?php echo($app_id); ?>, cookie: true, // enable cookies to allow the server to access the session oauth: true, // enable OAuth 2.0 xfbml: true // parse XFBML }); FB.getLoginStatus(function (res) { console.log(res.status); }); }; (function(d){ var js, id = 'facebook-jssdk'; if (d.getElementById(id)) {return;} js = d.createElement('script'); js.id = id; js.async = true; js.src = "//connect.facebook.net/en_US/all.js"; d.getElementsByTagName('head')[0].appendChild(js); }(document)); </script> </body> </html> 
+7
source share
2 answers

The reason you get stuck in the loop when your original token expires is because your code does not allow Facebook to give you a new token. When you get an OAuth error, you invoke the OAuth dialog box, which does not replace your cookie with a fresh token.

Another problem that you are facing is that you are rewriting a long-term access token with a short-term access token before calling the api:

 // known valid access token stored in a database $access_token = isset($_COOKIE["FB_LONG_AC_TOKEN"]) ? $_COOKIE["FB_LONG_AC_TOKEN"] : false; ... if ($access_token) { $facebook->setAccessToken($access_token); //Loads the short-term token from a cookie! 

If this were my code, I would use two variables: $access_token_temp and $access_token_long , so that I can keep everything in order.

EDIT

Your workflow when loading the page should be:

 + IF one exists, retrieve the long-term token from `$_COOKIE['FB_LONG_AC_TOKEN']` + If it does exist, test to see if it is valid. + If valid, use the renew the token and update the cookie if one is returned. (Only one token will be returned per day) + Else mark the long-term token as invalid and clear the cookie. + Set a `$vaild_user` flag. + ELSE IF a new short-term token is available + Read the short-term token cookie with the PHP API and exchange this for a long-term token. + Store the long-term token in the cookie + Clear the short-term token cookie + Set a `$valid_user` flag + ELSE: The token does not exist or is invalid + Redirect the user to the JS API auth dialog and store the returned short term token in a cookie. + Reload the page to process this. + ENDIF + IF `$valid_user`: Do stuff. 

You have all parts of your code. You just need to clear the logic for it to work.

Edit # 2:

I ran your code on my server. This basically works, but the debug information output sends the headers prematurely, setcookie() setting cookies.

I got your code to run by declaring $out = array(); next to the beginning, and then changing all your echo and print statements to $out[] = "What you were echoing or printing before"; . To still display this, add echo implode("\n", $out); in <body> your document.

As soon as I did this, I was able to save the valid token in a cookie, and verify that it is a really long-running token after 60 days.

+4
source

It seems that FB discourages us from doing server exchanges for longer tokens:

Re: Desktop applications: "However, there is no way to get long-lived user access to the domain without re-entering the user into your application." https://developers.facebook.com/roadmap/offline-access-removal/

Have you checked the time zone settings on your server? I have offline_access token settings with updating - although this is due to users re-clicking to update (not automatically).

In addition, you can easily make graphical calls with

 $facebook->api("me"); // no need to add access token, or decode JSON, etc. 
0
source

All Articles