PHP SDK: How do I capture the access token after user auths app?

Bill Scheurer picture Bill Scheurer · Sep 8, 2011 · Viewed 21.8k times · Source

This is for a canvas app on the Facebook Platform using the new(est) Facebook PHP SDK.

We are using the PHP example from the Facebook tutorial (https://developers.facebook.com/docs/appsonfacebook/tutorial/) to trigger the OAuth dialog and get the test user to the redirect URL.

At the redirect URL, we use the PHP example from the Facebook signed request docs page (https://developers.facebook.com/docs/authentication/signed_request/) and our test users can successfully authorize the app.

However, after the test user auths the app, we are not able to capture the access token and its expiration. We can see it in the address bar appended to the redirect URL, but it does not show up in the $_REQUEST array. If we add {$access_token = $facebook->getAccessToken();} to the redirect URL page, it shows a value for the access token, but the value it shows is not the full token string that we see when we click on Show Token in the Test User Roles page (which we believe is the correct access token for the test user).

Here is an example of the redirect URL with an access token appended: http://karmakorn.com/karmakorn/alpha20/kk-fb-auth.php#access_token=126736467765%7C2.AQDavId8oL80P5t9.3600.1315522800.1-100002908746828%7CJICJwM1P_97tKmqkEO5pXDCf-7Y&expires_in=6008

Here is what var_dump shows for the $REQUEST array for that same page: array(3) { ["_qca"]=> string(26) "P0-709927483-1291994912966" ["__switchTo5x"]=> string(2) "30" ["PHPSESSID"]=> string(26) "euois02ead39ijumca7nffblh2" }

We have no idea why the $_REQUEST array varies from the values appended to the URL, and more importantly -- how to capture the access token and its expiration date.

Can someone show us a working example of how they capture this data after running the parse_signed_request($signed_request, $secret) function on the redirect page? Thanks!

ADDITIONAL INFO:

Here is the pertinent code from A) our test index page, and B) our test redirect page. If we use our text index page as the redirect url it gets stuck in an endless loop -- because the user is never identified.

A) Index Page

// Create kk-fb app instance
$facebook = new Facebook(array(
    'appId'  => KKFB_ID,
    'secret' => KKFB_KY,
    'oauth' => true,
));

$app_id = KKFB_ID;
$secret = KKFB_KY;
$canvas_auth = 'http://karmakorn.com/karmakorn/alpha20/kk-fb-auth.php';

$auth_url = "https://www.facebook.com/dialog/oauth?" 
                . "client_id=" . $app_id 
                . "&redirect_uri=" . urlencode($canvas_auth) 
                . "&response_type=token" 
                . "&scope=email,publish_stream";

$signed_request = $_REQUEST["signed_request"];

list($encoded_sig, $payload) = explode('.', $signed_request, 2); 

$data = json_decode(base64_decode(strtr($payload, '-_', '+/')), true);

if (empty($data["user_id"])) {
    echo("<script> top.location.href='" . $auth_url . "'</script>");
} else {
    echo ("Welcome User: " . $data["user_id"]);
}

B) Redirect Page

// Create kk-fb app instance
$facebook = new Facebook(array(
    'appId'  => KKFB_ID,
    'secret' => KKFB_KY,
    'oauth' => true,
));

$app_id = KKFB_ID;
$secret = KKFB_KY;

$signed_request = $_REQUEST["signed_request"];

list($encoded_sig, $payload) = explode('.', $signed_request, 2); 

$data = json_decode(base64_decode(strtr($payload, '-_', '+/')), true);

$user = $facebook->getUser();
$access_token = $facebook->getAccessToken();

echo "User: $user <br>";
echo "Access Token: $access_token <br>";
echo "Signed Request: $signed_request <br>";
var_dump($_REQUEST);

Here is what shows up as these echo results:

User: 0 Access Token: 126736467765|**SECRET** Signed Request: array(3) { ["_qca"]=> string(26) "P0-709927483-1291994912966" ["_switchTo5x"]=> string(2) "30" ["PHPSESSID"]=> string(26) "frugi545cdl15gjind1fnv6pq1" }

Interestingly, when the test user goes back to the index page the if condition is satisfied and we can get the correct access token:

Welcome User: 100002908746828 Access Token: 126736467765|2.AQBgcyzfu75IMCjw.3600.1315544400.1-100002908746828|m5IYEm976tJAkbTLdxHAhhgKmz8

Obviously, we are still missing something!? Also, we need to learn how to get the expiration time as a variable too so we can store both of these in our database.

Answer

PCheese picture PCheese · Sep 9, 2011

OK, let's try this again.

Server-side vs Client-side Authentication

You are exclusively using the PHP SDK, so you want to do server-side authentication, where the authentication code is sent to the server over HTTP via the URL. This will allow you to fetch an access token for the user on the first page load after auth (in your case, the redirect page). The auth_url you are currently constructing is setting response_type=token, which forces the redirect to use client-side auth mode and set the token in the URL fragment instead of in the query. You should remove that parameter completely. In fact, I highly recommend you just use the PHP SDK instead of constructing that URL yourself. See example below.

Application Access Tokens

The odd-looking access token 126736467765|SECRET is your application access token, which is composed of your app ID and secret key. The application access token is returned by getAccessToken() if no user access token is available (because some API calls require at least some sort of access token). This also means that you've revealed your secret key to the world via this blog post, so you should reset your app secret otherwise anyone will be able to make API calls on your behalf. I highly recommend you elide parts of your access tokens if you share them with others.

Token Expiration

The OAuth 2.0 flow and v3.1.1 of the PHP SDK don't make determining the expiration time of a token all that easy. I would suggest attempting to make the API call, and then refreshing the token if the API call fails with an OAuthException. Tokens can be invalid even if they haven't expired, so this deals with more cases. However, if you still want to maintain the expiration date on your end, you might just want to extract it from the token itself. If you have an expiring token, then the expiration timestamp will be contained within that string. Here's a function I put together quickly to extract that:

function extractExpirationFromToken($access_token) {
    $segments = explode('|', $access_token);
    if(count($segments) < 2) { return 0; }

    $segments = explode('.', $segments[1]);
    if(count($segments) < 4) { return 0; }

    $expires = $segments[3];
    $dash_pos = strrpos($expires, '-');
    if($dash_pos !== false) {
        $expires = substr($expires, 0, $dash_pos);
    }
    return $expires;
}

New Index Page Code

// Create kk-fb app instance
$facebook = new Facebook(array(
    'appId'  => KKFB_ID,
    'secret' => KKFB_KY,
));

$canvas_auth = 'http://karmakorn.com/karmakorn/alpha20/kk-fb-auth.php';

$auth_url = $facebook->getLoginUrl(array(
    'scope' => 'email,publish_stream',
    'redirect_uri' => $canvas_auth, // you could just redirect back to this index page though
));

$user = $facebook->getUser();

if (empty($user)) {
    echo("<script> top.location.href='" . $auth_url . "'</script>");
} else {
    echo ("Welcome User: " . $user);
}

Redirect Page

I don't think you need this page at all. You could just redirect the user back to your original index page.

// Create kk-fb app instance
$facebook = new Facebook(array(
    'appId'  => KKFB_ID,
    'secret' => KKFB_KY,
));

$user = $facebook->getUser();
$access_token = $facebook->getAccessToken();
// also copy the function definition given earlier
$expiration = extractExpirationFromToken($access_token);

echo "User: $user <br>";
echo "Access Token: $access_token <br>";
echo "Expiration: $expiration <br>";
echo "Request: <br>";
var_dump($_REQUEST);