Asked  7 Months ago    Answers:  5   Viewed   38 times

i'm trying to get Facebook user id using the php sdk like this

$fb = new FacebookFacebook([
    'app_id' => '11111111111',
    'app_secret' => '1111222211111112222',
    'default_graph_version' => 'v2.4',
]);

$helper = $fb->getRedirectLoginHelper();


$permissions = ['public_profile','email']; // Optional permissions
$loginUrl = $helper->getLoginUrl('http://MyWebSite', $permissions);

echo '<a href="' . $loginUrl . '">Log in with Facebook!</a>';


    try {
        $accessToken = $helper->getAccessToken();
        var_dump($accessToken);
    } catch (FacebookExceptionsFacebookResponseException $e) {
        // When Graph returns an error
        echo 'Graph returned an error: ' . $e->getMessage();
        exit;
    } catch (FacebookExceptionsFacebookSDKException $e) {
        // When validation fails or other local issues
        echo '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 'Bad request';
        }
        exit;
    }

// Logged in
    echo '<h3>Access Token</h3>';
    var_dump($accessToken->getValue());

// The OAuth 2.0 client handler helps us manage access tokens
    $oAuth2Client = $fb->getOAuth2Client();

// Get the access token metadata from /debug_token
    $tokenMetadata = $oAuth2Client->debugToken($accessToken);
    echo '<h3>Metadata</h3>';
    var_dump($tokenMetadata);

// Validation (these will throw FacebookSDKException's when they fail)
    $tokenMetadata->validateAppId($config['11111111111']);
// If you know the user ID this access token belongs to, you can validate it here
//$tokenMetadata->validateUserId('123');
    $tokenMetadata->validateExpiration();

    if (!$accessToken->isLongLived()) {
        // Exchanges a short-lived access token for a long-lived one
        try {
            $accessToken = $oAuth2Client->getLongLivedAccessToken($accessToken);
        } catch (FacebookExceptionsFacebookSDKException $e) {
            echo "<p>Error getting long-lived access token: " . $helper->getMessage() . "</p>nn";
            exit;
        }

        echo '<h3>Long-lived</h3>';
        var_dump($accessToken->getValue());
    }

    $_SESSION['fb_access_token'] = (string)$accessToken;

but it give me this error:

Facebook SDK returned an error: 
Cross-site request forgery validation failed. 
The "state" param from the URL and session do not match.

please any help i'm new in php and Facebook sdk's thank for any help in advance.

 Answers

29

I found that as long as I enabled PHP sessions before generating the login url, and at the top of the script Facebook eventually redirects to, it works just fine on its own without setting a cookie (as per ale500's answer). This is using the 5.1 version of the sdk.

At the top of both scripts, I added...

if(!session_id()) {
    session_start();
}

...and it "just worked".

Here's a barebones complete example that worked for me:

auth.php

if (!session_id()) {
    session_start();
}

$oFB = new FacebookFacebook([
    'app_id'     => FACEBOOK_APP_ID,
    'app_secret' => FACEBOOK_APP_SECRET
]);

$oHelper = self::$oFB->getRedirectLoginHelper();
$sURL = $oHelper->getLoginUrl(FACEBOOK_AUTH_CALLBACK, FACEBOOK_PERMISSIONS);

// Redirect or show link to user.

auth_callback.php

if (!session_id()) {
    session_start();
}

$oFB = new FacebookFacebook([
    'app_id'     => FACEBOOK_APP_ID,
    'app_secret' => FACEBOOK_APP_SECRET
]);

$oHelper = self::$oFB->getRedirectLoginHelper();
$oAccessToken = $oHelper->getAccessToken();
if ($oAccessToken !== null) {
    $oResponse = self::$oFB->get('/me?fields=id,name,email', $oAccessToken);
    print_r($oResponse->getGraphUser());
}

Why?

As an additional note, this is explained in the Docs on the repo. Look at the warning on this page.

Warning: The FacebookRedirectLoginHelper makes use of sessions to store a CSRF value. You need to make sure you have sessions enabled before invoking the getLoginUrl() method. This is usually done automatically in most web frameworks, but if you're not using a web framework you can add session_start(); to the top of your login.php & login-callback.php scripts. You can overwrite the default session handling - see extensibility points below.

I'm adding this note because it's important to keep in mind should you happen to be running your own session management or if you're running multiple web servers in parallel. In those cases, relying upon php's default session methods won't always work.

Wednesday, March 31, 2021
 
PLPeeters
answered 7 Months ago
62

I got with the same problem. After looking for a solution I found that FB silently killed public RSS support. (see this post from Jesse Stay)

I understood that I needed to call the API myself and construct the feed (I also need the feed to be parsed by a WP plugin and other stuff.

So, first of all get an API key (also called app id) and download the PHP Facebook SDK.

Then download the Universal Feed Generator PHP class. It will generate all the required headers and xml for you.

Your php script will be like this:

require('lib/facebook.php'); // require your facebook php sdk
include("feed_generator/FeedWriter.php"); // include the feed generator feedwriter file

$fb = new facebook(array(
    'appId' =>  'YOUR_APP_ID', // get this info from the facebook developers page
    'secret'=>  'YOUR_SECRET_KEY' // by registering an app
));
$response = $fb->api('/spreetable/feed','GET'); // replace "spreetable" with your fb page name or username

// create the feedwriter object (we're using ATOM but there're other options like rss, etc)
$feed = new FeedWriter(ATOM);

$feed->setTitle('Spree Table'); // set your title
$feed->setLink('http://spreetable.com/facebook/feed.php'); // set the url to the feed page you're generating

$feed->setChannelElement('updated', date(DATE_ATOM , time()));
$feed->setChannelElement('author', array('name'=>'Spree Table')); // set the author name

// iterate through the facebook response to add items to the feed
foreach($response['data'] as $entry){
        if(isset($entry["message"])){
            $item = $feed->createNewItem();
            $item->setTitle($entry["from"]["name"]);
            $item->setDate($entry["updated_time"]);
            $item->setDescription($entry["message"]);
            if(isset($entry["link"]))
                $item->setLink(htmlentities($entry["link"]));

            $feed->addItem($item);
        }
}

// that's it... don't echo anything else, just call this method
$feed->genarateFeed();

Note from the future (2013-07-09): Don't listen to my answer anymore. It's old. Facebook has a new API with new features on its query language so don't bother pulling feeds. Try to use their API in a more fun, intelligent way :)

Wednesday, March 31, 2021
 
hillz
answered 7 Months ago
65

Is this a good way to prevent CSRF?

Yes. What this does is to force the client to do a GET on the form before it can do a POST to your form handler. This prevents CSRF in modern browsers since browsers will prevent client-side Javascript to do an XHR GET request to a foreign domain, so a 3rd party cannot imitate your form on their site and successfully get a valid token for the submission.

When another page is opened as well that sets the same $_SESSION variable, the previous (still open) page becomes invalid, how to prevent this?

Allow several tokens to be valid at a time, keeping an array of valid tokens in the session. Alternatively, store no tokens at all and use a token signing scheme instead. I've dabbled in and explained that here. Alternative 2: just use a single token for the whole session, without invalidating tokens. (tip o' the hat to @SilverlightFox in the comments)

For forms this method is clear, but how to handle normal links? Is it necessary to append the token to each link as well?

No. You only need to protect POST requests since presumably only POST requests can alter sensitive data (wink wink nudge nudge, you're sticking to REST conventions, right?!) and XHR GET requests are already blocked browser-side.

Wednesday, March 31, 2021
 
Kevin_Kinsey
answered 7 Months ago
93

Finally found the reasoning behind this.

After the move to Cloud hosting, from Shared hosting, I had updated my DNS records. However, I never updated my IPv6 record.

This wouldn't normally be an issue (and it explains why 99% of websites/services had no issue scraping/using my site) but Facebook appears to prioritise IPv6 over everything. So it was using my IPv6 record which was pointing to my old server and an old version of my website. Hence, it was pulling in no information for the page.

I'm surprised it was still linking to the new page after we manually put in the image and title when posting to Facebook.

I spotted this after my Share hosting plan was officially cut off today, therefore deleting my old website. The links began showing a 404 error.

Hopefully this can help others, as I've seen a lot of people with a similar issue but no solution. Update your IPv6 record as Facebook uses it!

Friday, July 30, 2021
 
Grokodile
answered 3 Months ago
96

The Struts 1 Action token methods work like the Struts 2 token interceptor in that it will add a token to your session and check it on form submission, but it is a much more manual process. The basic workflow is:

  1. The user gets to the form through a Struts Action (not directly to the JSP). The Struts Action will call saveToken(request) before forwarding onto the JSP that contains the form.
  2. The form on the JSP must use the <html:form> tag.
  3. Your Action that the form submits to will first call isTokenValid(request, true), and you should redirect back to the first Action with an error message if it returns false. This also resets the token for the next request.

Doing this will not only prevent duplicate form submissions but any script will have to hit the first Struts Action and get a session before it can submit to the second Struts Action to submit the form. Since a site can't set a session for another site, this should prevent CSRF.

If you usually send users directly to your JSP, don't. Instead, create a new class inheriting from ActionForward and set this as it's execute() method:

public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response)  throws Exception {
    saveToken(request);
    return super.execute(mapping, form, request, response);
}
Tuesday, September 14, 2021
 
ammezie
answered 1 Month ago
Only authorized users can answer the question. Please sign in first, or register a free account.
Not the answer you're looking for? Browse other questions tagged :
 
Share