Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Facebook SDK access tokens and ajax

I followed the Facebook SDK for PHP docs and created two files, login.php and fb-callback.php, with all the lookup logic in fb-callback.php. When I do this, everything works fine.

But I want to move the lookup logic to get-posts.php and call it via ajax from fb-callback.php. When I do so, I can't seem to get the access token. I get the error noted below, "Access Token: Bad request".

I have registered both fb-config.php and get-posts.php as Valid OAuth Redirect URIs. So how do I get the proper parameters to get-posts.php?

Here are all the associated files:

login.php

<?php
require_once "config.php";

$redirectURL = 'https://' . $_SERVER[ 'SERVER_NAME' ] . '/r/fb-callback.php';
$permissions = ['email','user_photos','user_posts'];
$loginUrl = $helper->getLoginUrl($redirectURL, $permissions);
?>

<a href='<?php echo $loginUrl; ?>'>
<img src='continue-with-facebook.png'>
</a>
?>

config.php

<?php
if( !session_id() ) {
    session_start();
}

require_once '/home/bitnami/vendor/autoload.php';

$fb = new Facebook\Facebook([
  'app_id' => '---',
  'app_secret' => '---',
  'default_graph_version' => 'v3.1',
  ]);

$helper = $fb->getRedirectLoginHelper();
?>

fb-callback.php

<?php
require_once("config.php");
?>
<html>
<head>
  <script type='text/javascript' src='jquery.js'></script>
  <script>
    var $j = jQuery.noConflict();

    $j(document).ready(function () {

      $j.ajax({
        type: "GET",
        url: "get-posts.php",
        cache: false,
        success: function (html) {
          setTimeout(function () {
            $j('#updateDiv').html(html);
          }, 1000);
        }
      });
    });
  </script>
    </head>
    <body>
        <div id='updateDiv'><img src='spinning.gif' alt='processing...'></div> 
    </body>
</html>

get-posts.php

<?php
require_once("config.php");

// get the posts for this user id
try {
  $accessToken = $helper->getAccessToken();
} catch(Facebook\Exceptions\FacebookResponseException $e) {
  // When Graph returns an error
  echo __LINE__ . ' Access Token: Graph returned an error: ' . $e->getMessage();
  exit;
} catch(Facebook\Exceptions\FacebookSDKException $e) {
  // When validation fails or other local issues
  echo __LINE__ . ' Access Token: Facebook SDK returned an error: ' . $e->getMessage();
  exit;
}

if (! isset($accessToken)) {
  if ($helper->getError()) {
    header('HTTP/1.0 401 Unauthorized');
    echo "Error: " . $helper->getError() . "\n";
    echo "Error Code: " . $helper->getErrorCode() . "\n";
    echo "Error Reason: " . $helper->getErrorReason() . "\n";
    echo "Error Description: " . $helper->getErrorDescription() . "\n";
  } else {
    header('HTTP/1.0 400 Bad Request');
    echo __LINE__ . ' Access Token: Bad request';
  }
  exit;
}

...
like image 789
Kenneth Vogt Avatar asked Oct 11 '18 05:10

Kenneth Vogt


2 Answers

Here is the actual code that works with explanations to what had to change below:

fb-callback.php

<?php
require_once("config.php");

try {
  $accessToken = $helper->getAccessToken();
} catch(Facebook\Exceptions\FacebookResponseException $e) {
  // When Graph returns an error
  echo __LINE__ . ' Access Token: Graph returned an error: ' . $e->getMessage();
  exit;
} catch(Facebook\Exceptions\FacebookSDKException $e) {
  // When validation fails or other local issues
  echo __LINE__ . ' Access Token: Facebook SDK returned an error: ' . $e->getMessage();
  exit;
}

if (! isset($accessToken)) {
  if ($helper->getError()) {
    header('HTTP/1.0 401 Unauthorized');
    echo "Error: " . $helper->getError() . "\n";
    echo "Error Code: " . $helper->getErrorCode() . "\n";
    echo "Error Reason: " . $helper->getErrorReason() . "\n";
    echo "Error Description: " . $helper->getErrorDescription() . "\n";
  } else {
    header('HTTP/1.0 400 Bad Request');
    echo __LINE__ . ' Access Token: Bad request';
  }
  exit;
}

$get = "?accessToken=" . $accessToken;
?>
<html>
<head>
  <script type='text/javascript' src='jquery.js'></script>
  <script>
    var $j = jQuery.noConflict();

    $j(document).ready(function () {

      $j.ajax({
        type: "GET",
        url: "get-posts.php<?php echo $get; ?>",
        cache: false,
        success: function (html) {
          setTimeout(function () {
            $j('#updateDiv').html(html);
          }, 1000);
        }
      });
    });
  </script>
    </head>
    <body>
        <div id='updateDiv'><img src='spinning.gif' alt='processing...'></div> 
    </body>
</html>

get-posts.php

<?php
require_once("config.php");

$fb->setDefaultAccessToken($_GET['accessToken']);

...

So what changed? First, I needed to get the access code in fb-callback.php, not get-posts.php. So I moved the logic to get the access code and check for errors back to fb-callback.php. Then I added some logic to pass the access token collected to the parameter string of the AJAX url. (Look for "$get" in two places, once where I set the value and once where I attach it to the AJAX url.)

In get-posts.php, I removed the code that asks the Facebook API for an access token. Instead, I merely set the access token based on what was passed from fb-config.php via GET in the parameter named "accessToken" with the setDefaultAccesstoken() function from the Facebook PHP SDK.

As noted elsewhere, you can keep passing along the value of the access token via GET, REQUEST, or $_SESSION to page after page, so long as you maintain a session and inform Facebook via the setDefaultAccessToken() function at each subsequent page.

like image 198
Kenneth Vogt Avatar answered Oct 19 '22 02:10

Kenneth Vogt


The answer is in the comments, but there are actually better ways.


Firstly, to answer your question directly, the issue is that the "code" passed to the fb-callback.php has not been passed to the script that calls $helper->getAccessToken()

The docs for that function (at https://developers.facebook.com/docs/php/FacebookRedirectLoginHelper/5.0.0) states

Attempts to obtain an access token from an authorization code. This method will make a request to the Graph API and return a response. If there was an error in that process a FacebookSDKException will be thrown. A FacebookSDKException will also be thrown if the CSRF validation fails.

If no authorization code could be found from the code param in the URL, this method will return null.

There are two other points to note:

  • The "state" (cross site forgery token) is part of the session; you do not need to explicitly pass it in so long as you're on the same domain, however you may need session_start() in both login and get-posts scripts There is a parameter to the getAccessToken function which should be the URL of the ORIGINAL

So very simply

  1. Add session_start() to the login script and the get-posts.php
  2. Modify the fb-callback.php to be something like

.

<?php
require_once("config.php");
$code = $_GET['code'];   // warning: add validation code exists, sanitise etc.
?>
<html>
<head>
  <script type='text/javascript' src='jquery.js'></script>
  <script>
    var $j = jQuery.noConflict();

    $j(document).ready(function () {

      $j.ajax({
        type: "GET",
        url: "get-posts.php?code=<?php echo $code; ?>",
        cache: false,
        success: function (html) {
          setTimeout(function () {
            $j('#updateDiv').html(html);
          }, 1000);
        }
      });
    });
  </script>
    </head>
    <body>
        <div id='updateDiv'><img src='spinning.gif' alt='processing...'></div> 
    </body>
</html>

(Note: I hate inline PHP - this is example only)


However: what you should be doing is passing the Access Token between functions and not the oAuth code. The way you have it above (passing the oAuth code) is a one-off; however you can call get-posts a second/third/fourth time and it'll work if you have the access token first.

1) Do the token exchange in fb-callback.php at the top (it's fast, no need for spinner), grab the token (which FB library stores in a session variable/cookie, or you can do it yourself) and then show the HTML page with spinner and AJAX call.

2) Do the token exchange in fb-callback.php at the top (it's fast, no need for spinner), grab the token (which FB library stores in a session variable/cookie, or you can do it yourself) and then quickly redirect to another page that shows the spinner and does AJAX call.

The critical think to note in both of these is you are passing the Access Token; in get-posts.php check "do I have access token; if not - show login button; if so - call facebook".

like image 41
Robbie Avatar answered Oct 19 '22 03:10

Robbie