So I'm fairly new to Facebook dev and PHP dev so please bare with.
I've created an iframe Facebook application. I have no trouble grabbing a signed request and using that information to do basic tasks on my page.
The real problem happens when I start navigating around my application within Facebook. I seem to lose my signed request information.
I have my PHP code included on every page and I still can't retain my signed request or session.... whatever its called.
I've pasted my basic PHP code below.
<?php
include_once "../lib/facebook.php";
$facebook = new Facebook(array(
"appId" => FACEBOOK_APP_ID,
"secret" => FACEBOOK_SECRET_KEY,
"cookie" => true,
"domain" => SERVER_DOMAIN
));
function parse_signed_request($signed_request, $secret) {
list($encoded_sig, $payload) = explode(".", $signed_request, 2);
// decode the data
$sig = base64_url_decode($encoded_sig);
$data = json_decode(base64_url_decode($payload), true);
if (strtoupper($data["algorithm"]) !== "HMAC-SHA256") {
error_log("Unknown algorithm. Expected HMAC-SHA256");
return null;
}
// check sig
$expected_sig = hash_hmac("sha256", $payload, $secret, $raw = true);
if ($sig !== $expected_sig) {
error_log("Bad Signed JSON signature!");
return null;
}
return $data;
}
function base64_url_decode($input) {
return base64_decode(strtr($input, "-_", "+/"));
}
session_start();
if (isset($_SESSION['fb_data'])) {
$data = $_SESSION['fb_data'];
} else {
$data = parse_signed_request($_REQUEST["signed_request"], FACEBOOK_SECRET_KEY);
$_SESSION['fb_data'] = $data;
}
session_write_close();
$page_id = $data["page"]["id"];
$like_status = $data["page"]["liked"];
?>
The signed_request is only passed into your page when it is first loaded as a Facebook Canvas page. The signed_request is passed in as a POST variable. If you are navigating to different pages at the iframe level instead of the top level, you will lose that signed_request parameter.
For example, when user loads this page:
http://apps.facebook.com/test_app/
Facebook will load this inside its IFrame:
http://yourserver.com/test_app/
while passing in signed_request as a POST variable.
If you then navigate to different pages using the following tag:
Page 2
signed_request parameter will be lost.
Instead, you can either pass through the signed_request parameter as a GET variable:
Page 2
Or reload the page at top level:
<a target="_top" href="http://apps.facebook.com/test_app/page2"> Page 2 </a>
In case someone stumbles on this, the latest version of Facebook's php-sdk automatically uses $_SESSION variables to persist the signed_request when it isn't passed in on a Canvas page or Tab request. https://github.com/facebook/php-sdk
Related
My search has come up empty because when you search for "Facebook" and "like" I get all kinds of other results.
I have an app that is only on my company's Facebook page. In that app I need to find out if the user has liked the company's page. I'll show one thing if not and another thing if so. How can I do this using the Facebook PHP SDK v.3.1.1?
It can be done this way:
<?php
require('facebook.php');
$config = array(
'appId' => 'your facebook app id',
'secret' => 'your facebook app secret code',
'allowSignedRequest' => false
);
$facebook = new Facebook($config);
$user_id = $facebook->getUser();
if (isset($user_id)) {
try {
$likes = $facebook->api('/me/likes/your_facebook_page_id_here', 'GET');
if (!empty($likes['data'])) // if user has liked the page then $likes['data'] wont be empty otherwise it will be empty
{
echo 'Thank you for liking our fan page!';
}
else {
//show something else
}
} catch (FacebookApiException $e) {
$login_url = $facebook->getLoginUrl();
echo 'Please click here to login into your Facebook account.';
error_log($e->getType());
error_log($e->getMessage());
}
} else {
$login_url = $facebook->getLoginUrl();
echo 'Please lick here to login into your Facebook account';
}
?>
The user will click on the "Please click here to login into your Facebook account." text which will redirect it to Facebook app permissions page, once user allows the permission to your app the code will fetch user's data and will display what ever you want if user hasn't liked your fan page.
You can do this using FQL. You'll also need to make sure that you have the user_likes permission set.
I pulled this example from an older app that is now offline, it may need to be changed depending on what Facebook has changed in their last round of updates. Lately I've been using javascript and I subscribe to the edge.create event.... just replace the page_id with your page's id and give it a try
$checkIfUserLikePage = $facebook->api(array(
"method" => "fql.query",
"query" => "select uid from page_fan where uid=me() and page_id=1234567"
));
$checkIfUserLikePage = sizeof($checkIfUserLikePage) == 1 ? true : false;
This should work for you! I had to do a lot of these types of pages so I created a really simple way of creating like gated pages. https://github.com/DrewDahlman/FBVersion
Enjoy!
$signed_request = $_REQUEST['signed_request'];
function parsePageSignedRequest() {
if (isset($_REQUEST['signed_request'])) {
$encoded_sig = null;
$payload = null;
list($encoded_sig, $payload) = explode('.', $_REQUEST['signed_request'], 2);
$sig = base64_decode(strtr($encoded_sig, '-_', '+/'));
$data = json_decode(base64_decode(strtr($payload, '-_', '+/'), true));
return $data;
}
return false;
}
if($signed_request = parsePageSignedRequest()) {
if($signed_request->page->liked) {
$this->assign('verify', $signed_request->page->liked);
}
else {
echo "Please click on the Like button to view our Coupons!";
jexit();
}
}
I hope this will help you :)
Old topic, but wanted to weigh in as I have recently learned a lot about how to do this and have had to put it into practice.
Have a look at this link: http://nickring.com/blog/2012/11/facebook-fan-gate-using-php/. It uses the signed_request variable as some of the other responses show, but it shows how it does not require requesting the signed_request variable via $_REQUEST.
The one main thing to remember is that signed_request is only available when the PHP script accessing signed_request is run within Facebook. If you run this script outside of Facebook in a script attempting to use the Facebook API, it will return an empty array.
Here's an example - the following script will run when you go to this address: https://www.facebook.com/yourFacebookPage/app_xxxxxxxxxxxxxxx with 'xxxxxxxxxxxxxxx' being the app ID.
// Check if the user has liked us on Facebook, require the Facebook SDK
require_once('linked/facebook/facebook.php');
// Setup the Config
$config = array(
'appId' => 'xxxxxxxxxxxxxxxxx',
'secret' => 'yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy',
'cookie' => true
);
// Create a Facebook SDK instance
$facebook = new Facebook($config);
// Get the signed_request variable that we so desparately need
$signed_request = $facebook->getSignedRequest();
$like_status = $signed_request["page"]["liked"];
// Make sure that it worked
if (!empty($signed_request))
{
// Get the signed_request information
if ($like_status == 1)
{
// Wo0t! Show the FB fan only page stuff here
}
else
{
// Show the 'Please like us page'
$page = file_get_contents('pages/facebookLikeUs.html');
// Finish the page
echo $page;
exit();
} // End if ($like_status == 1) ELSE Clause and IF
} // End if (!empty($signed_request)) IF Clause
else
{
// Damn, it didn't work. Show an error
}
The above script is the script that is called from the URL set in the Canvas URL in the "App on Facebook" section of the App's settings. The facebookLikeUs.html page is simply a page asking them to click "Like" to continue. If I'm in a situation that I want them to be redirected back into a website that requires the Facebook like I simply replace the // Wo0t! section with something like this:
// Wo0t! They're all set, establish some cookies, get a cookie, and redirect back to the PHD program site
setcookie('fbls', $signed_request['page']['id'] . '-' . $signed_request['user_id'], time() + 300);
$redirectURL = "http://www.myurl.com/theScriptIWantToReceiveTheUserFromFB.php";
// Since Facebook really wants to keep us in the page, we need to create a page that will automatically break out of FB
$page = file_get_contents('pages/facebookRedirectBack.html');
// Replace some stuff
$page = str_replace('$redirectURL', $redirectURL, $page);
// Output the page
echo $page;
exit();
With the facebookRedirectBack.html page being this:
<!DOCTYPE html>
<html>
<body>
<script type="text/javascript">
window.top.location.href = '$redirectURL';
</script>
</body>
</html>
If you're looking to have a bit more security on this, you can have the redirecting PHP script write some cookies and attach them to the URL such that the receiving script must compare the cookies and the URL params then delete the cookies after they've been read.
Hope this helps as I personally haven't found any consistent information on this subject.
We developed a facebook application as tab application using FBML. Now Facebook had deprecated the fbml and we are converting our application to iframe. For this the user can create a FB page and add the application to their facebook page. We are taking the user's facebook page id and check in the DB with this page id and showing the data for that particular user. In the FBML tab application we are getting the facebook page id as a request($REQUEST['page_id']). This seems to no longer work.
When we convert the application to IFRAME tab application, FB is not providing the page id. We are not using GRAPH API and there is any authentication before viewing the application. I had read from a forum that facebook signed request, we will get the page id and used the following code and not getting any data.
$sgrequest = $_REQUEST['signed_request'];
$requestVal = parse_signed_request($sgrequest,$secret);
print_r($requestVal);
$_REQUEST['signed_request']['app_data']
function parse_signed_request($signed_request, $secret) {
list($encoded_sig, $payload) = explode('.', $signed_request, 2);
// decode the data
$sig = base64_url_decode($encoded_sig);
$data = json_decode(base64_url_decode($payload), true);
if (strtoupper($data['algorithm']) !== 'HMAC-SHA256') {
error_log('Unknown algorithm. Expected HMAC-SHA256');
return null;
}
// check sig
$expected_sig = hash_hmac('vctly987', $payload, $secret, $raw = true);
if ($sig !== $expected_sig) {
error_log('Bad Signed JSON signature!');
return null;
}
return $data;
}
function base64_url_decode($input) {
return base64_decode(strtr($input, '-_', '+/'));
}
Is there any other way to get the page id of the fan page through javascript or any other method?
have you tried printing out $requestVal[page][id] after parsing the signed request?
according to the documentation, the signed request contains an array called 'page' that contains the key 'id'. This stores the value of the page's id number.
Can you make sure you enable the migration setting signed_request for Canvas in your App https://developers.facebook.com/apps/.
App Settings -> Advanced
I've created a iframe facebook app and i want to disable the app outside of facebook, that the user can't open the app via direct url. I found a solution with javascript, but i need it in PHP.
I can't use the referrer, because some user have disabled it in there browser...
You can check if there's signed_request param sent in request.
If application is opened inside facebook than signed_request exists.
But there's one more issue.
You should check if signed_request is valid and for that you can use parse_signed_request method
public function parse_signed_request($signed_request, $secret) {
list($encoded_sig, $payload) = explode('.', $signed_request, 2);
// decode the data
$sig = $this->base64_url_decode($encoded_sig);
$data = json_decode($this->base64_url_decode($payload), true);
if (strtoupper($data['algorithm']) !== 'HMAC-SHA256') {
error_log('Unknown algorithm. Expected HMAC-SHA256');
return null;
}
// check sig
$expected_sig = hash_hmac('sha256', $payload, $secret, $raw = true);
if ($sig !== $expected_sig) {
error_log('Bad Signed JSON signature!');
return null;
}
return $data;
}
Then you can check if $data['user_id'] exist or is it equal to logged in user.
If not you can redirect like this
echo "< script type='text/javascript' >top.location.href = '$this->loginUrl'; < /script >";
Or find some way to redirect from php. (There was redirect method in the old php sdk)
Your only real option would be to attempt to use the Facebook API somehow, and see if you get any response.
I want to read all birthdays of the friends from current user. I use the new Graph API of facebook. I request the authorization of the permissions (read_friendslist and friends_birthday) based on Facebooks insights example and php-sdk example. For reading the friendslist and the user details I used the Graph API with Facebook PHP SDK.
The upcoming code snippets are a short self contained correct example of my approach. If I try to use my app it requests login, then asks for permissions and then fails in printing all my friends due to the fact that no session is available. What's wrong here?
First is the birthday.php which is used by the following index.php, I removed some boilerplate code or code I think it's not causing this problem (identified by [...]). You can find the complete code on the end of this question.
<?php
function get_birthday_of_friends() {
$fbconfig['appid' ] = "MY_APP_ID";
$fbconfig['secret'] = "MY_APP_SECRET";
try{
include_once "facebook/src/facebook.php";
}
catch(Exception $o){
// [...] log error
}
// Create our Application instance.
$facebook = new Facebook(array(
'appId' => $fbconfig['appid'],
'secret' => $fbconfig['secret'],
'cookie' => true,
));
$session = $facebook->getSession();
$fbme = null;
// Session based graph API call.
if ($session) {
// [...] return birthdays
} else {
echo "No session found";
}
}
?>
The required lib.php is identically with the insights example.
<?php
// [...] Include and define app-id etc.
function get_access_token($base_url) {
if (isset($_REQUEST['access_token'])) {
return $_REQUEST['access_token'];
}
$params = array();
$params['client_id'] = APP_ID;
$params['redirect_uri'] = $base_url;
if (!isset($_REQUEST['code'])) {
$params['scope'] = 'read_friendlists, friends_birthday';
$url = FacebookMethods::getGraphApiUrl('oauth/authorize', $params);
throw new RedirectionException($url);
} else {
$params['client_secret'] = APP_SECRET;
$params['code'] = $_REQUEST['code'];
$url = FacebookMethods::getGraphApiUrl('oauth/access_token');
$response = FacebookMethods::fetchUrl($url, $params);
$response = strstr($response, 'access_token=');
$result = substr($response, 13);
$pos = strpos($result, '&');
if ($pos !== false) {
$result = substr($result, 0, $pos);
}
return $result;
}
}
// [...] Call get_access_token() and get_birthday_of_friends()!
?>
Can you help me with that? I added the whole source code on pastebin.com if this helps you to identify my problem. Source code on pastebin.com for "index.php" and "birthday.php".
Thank you in advance!
I am not sure if the method that you are using is deprecated or not, but I know it's the old way and you should try with the new one in order to get the auth token.
Take a look at this link:
http://developers.facebook.com/docs/authentication/signed_request/
In a glance, you have to:
Get the signed_request parameter from $_REQUEST.
Use the sample function provided in
the link to decode it Once you decode
it, you will have an array in which
there is a parameter called
oauth_token.
With this parameter, you can start
making calls to the Graph by
appending it to the URL e.g.
*https://graph.facebook.com/PROFILE_ID/pictures/?access_token=OAUTH_TOKEN*
Make sure that you have Oauth 2.0 for Canvas enabled into the Configuration settings of your app (Advanced tab).
I think in some browsers there's a prblem with third party cookies. Are you testing in Safari? And also, try to add permissions to the loginUrl - it's a bit more simple than adding and requesting the permissions with oauth.
If no session is available, I had to redirect to the login page and require the extended permissions with the parameters. This did the trick to me, thanks to manuelpedrera for helping me out.
$facebook->getLoginUrl(array('req_perms' => 'read_friendlists, [...]'));
when user accepts the facebook application from my website, I am storing the user details and facebook details(access token) in database.
when he removes my application from facebook i want to remove the detail from database. how to do this?
I can give Deauthorize Callback url. if some one removes application, it will redirect to this page. but, wt should be the code here to delete the data from db? I means, when it redirect, will it post the access token details, so that i can charge fro access token and delete that row.
It's clearly stated in the authentication document:
App Deauthorization
When a user of your app removes it in
the App Dashboard or blocks the app in
the News Feed, your app can be
notified by specifying a Deauthorize
Callback URL in the Developer App.
During app removal we will send an
HTTP POST request containing a single
parameter, signed_request, which
contains the user id (UID) of the user
that just removed your app. You will
not receive an user access token in
this request and all existing user
access tokens will be automatically
expired.
So using the signed_request function on its own docuement:
<?php
function parse_signed_request($signed_request, $secret) {
list($encoded_sig, $payload) = explode('.', $signed_request, 2);
// decode the data
$sig = base64_url_decode($encoded_sig);
$data = json_decode(base64_url_decode($payload), true);
if (strtoupper($data['algorithm']) !== 'HMAC-SHA256') {
error_log('Unknown algorithm. Expected HMAC-SHA256');
return null;
}
// check sig
$expected_sig = hash_hmac('sha256', $payload, $secret, $raw = true);
if ($sig !== $expected_sig) {
error_log('Bad Signed JSON signature!');
return null;
}
return $data;
}
function base64_url_decode($input) {
return base64_decode(strtr($input, '-_', '+/'));
}
$result = parse_signed_request($_REQUEST['signed_request'],"APP_SECRET");
$myFile = "deauthorize.txt";
$fh = fopen($myFile, 'w') or die("can't open file");
fwrite($fh, $result["user_id"] . "\n");
fclose($fh);
?>
So all you need to do is get the $result["user_id"] query your DB and remove the record.
P.S: I would recommend adding a new field called active and just deactivate the user instead of removing the record all together.
EDIT:
Facebook will NOT redirect the user to the deauthorize URL! it'll ping it only:
Facebook pings this URL when a user
deauthorizes your app
This code is not working, although it gets called when a user deauthorizes the app the only data passed into the function is a "1" (Tested with test user and my own FB account when app is live)
file_put_contents("test.txt",$fbUserId . " ". print_r($_REQUEST['signed_request']));
in test.txt file " 1"