2012-05-30 18 views
7

Sto tentando di utilizzare un token di accesso di lunga durata memorizzato, ma dopo 2 ore ottengo il seguente errore dall'API del grafico. Ho scritto del codice per inviare l'utente a Facebook per ottenere un nuovo codice che può essere scambiato per un token di accesso, che funziona bene tranne che accade a ogni richiesta successiva, Facebook continua a invalidare il mio token di accesso con l'errore seguente, nonostante tale token di accesso sia restituito dal loro server.Facebook PHP/JS SDK "La sessione è scaduta in unix time" con token di durata lunga

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

L'esempio di pagina di prova completo è riportato di seguito. Omettere le mie chiavi per ovvi motivi. Colpisci la pagina, accedi e lasciala per qualche ora e torna alla pagina (riceverai il reindirizzamento a Facebook e torna con il codice nell'URL), ricarica la pagina e continuerà a reindirizzare a Facebook e ritorno , anche se sto dicendo di usare il token di accesso che è appena tornato per il codice.

<?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's 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's 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> 

Cosa sto sbagliando? o questo è un problema con Facebook?

UPDATE:

ho aggiornato il mio codice di seguire il flusso inviato da @cpilko tuttavia ancora sto avendo lo stesso problema. Posso accedere e uscire bene. Tuttavia, se visito la pagina di test dopo alcune ore, dire che il giorno successivo ottengo l'eccezione Session expired con il token di accesso long-life fornito (FB JS SDK pensa che io sia connesso, ma il server no), aggiorno la pagina e Mi viene mostrato come effettuato il login sia dal server che dall'SBK JS dell'FB e il token long life che ottengo da facebook è lo stesso di quello che ho provato (memorizzato nei miei cookies). Quello che non capisco è il motivo per cui non sono in grado di usare il token di lunga vita la prima volta. Codice aggiornato qui sotto

<?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's 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's 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's 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> 
+0

Hai provato a rimuovere e reinstallare l'app nel tuo account personale? –

risposta

4

La ragione si rimane bloccati nel ciclo quando il token scade originale è il codice non sta permettendo Facebook per darvi un nuovo token. Quando ricevi un errore OAuth, stai invocando la finestra di dialogo OAuth, che non ripopola il tuo cookie con un nuovo token.

L'altro problema che ho è che stai sovrascrivendo il token di accesso a lungo termine con la vostra token di accesso a breve termine prima di effettuare una chiamata 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! 

Se fosse il mio codice, userei due variabili: $access_token_temp e $access_token_long solo così potrei mantenere le cose dritte.

EDIT

Il flusso di lavoro al caricamento della pagina deve essere:

+ 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. 

Hai tutte le parti nel codice. Hai solo bisogno di ripulire la logica per farlo funzionare.

Edit # 2:

ho incontrato il codice sul mio server. Funziona principalmente, ma le informazioni di debug che stai trasmettendo inviano prematuramente le intestazioni, inibendo la capacità di setcookie() di impostare un cookie.

ho ottenuto il vostro codice per eseguire dichiarando $out = array(); vicino all'inizio, poi cambiando tutte le echo e print dichiarazioni di $out[] = "What you were echoing or printing before"; Per visualizzare ancora, aggiungere echo implode("\n", $out); all'interno del <body> del documento.

Una volta eseguita questa operazione, sono stato in grado di memorizzare un token valido in un cookie e validate che è effettivamente un token di lunga durata con una scadenza di 60 giorni.

+0

Il reindirizzamento è il modo documentato di Facebook per gestire i token scaduti. http://developers.facebook.com/blog/post/2011/05/13/how-to--handle-expired-access-tokens/ – Gcoop

+0

Sì, questo è un modo. Hai anche un altro modo documentato per re-autorizzare un utente nel tuo codice usando l'SDK JS http://developers.facebook.com/blog/post/534/. Il metodo che ho commentato non dà al tuo codice un token da rinnovare. – cpilko

+0

Quello era il mio codice precedente, ma causa il caricamento della pagina, e poi quando js viene ricaricato (per accedere automaticamente all'utente) ogni volta che l'utente visita il sito dopo due ore. Sopra sembrava essere la soluzione per accedere automaticamente al lato utente del server senza che vedessero una pagina a metà del caricamento e quindi il caricamento. – Gcoop

0

Sembra FB noi è dissuadere dal fare server-scambi per i token più lunghi:

Re: applicazioni desktop: "Tuttavia, non v'è alcun modo per ottenere un access_token utente longeva senza avere il login al app di nuovo. " https://developers.facebook.com/roadmap/offline-access-removal/

Avete controllato le impostazioni del fuso orario sul vostro server? Ho installato i token offline_access, con un aggiornamento, anche se gli utenti facevano nuovamente clic per aggiornare (non automatico).

Inoltre- si può fare chiamate grafico abbastanza facilmente con

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