13

I did a very simple PHP script, just to try to login via Facebook and get an accessToken. But when I try the following code, I get an Exception from the SDK : « Cross-site request forgery validation failed. Required param "state" missing. ».

Here is my code :

require_once __DIR__ . '/facebook-sdk-v5/autoload.php';
session_start();

$fb = new Facebook\Facebook([
    'app_id' => '{my-own-app-id}',
    'app_secret' => '{my-own-app-secret}'
]);

// Check to see if we already have an accessToken ?
if (isset($_SESSION['facebook_access_token'] )) {
    $accessToken = $_SESSION['facebook_access_token'];
    echo "Horray we have our accessToken:$accessToken<br />\n";

} else {
    // We don't have the accessToken
    // But are we in the process of getting it ? 
    if (isset($_REQUEST['code'])) {
        $helper = $fb->getRedirectLoginHelper();
        try {
            $accessToken = $helper->getAccessToken();
            } catch(Facebook\Exceptions\FacebookResponseException $e) {
              // When Graph returns an error
              echo 'Graph returned an error: ' . $e->getMessage();
              exit;
        } catch(Facebook\Exceptions\FacebookSDKException $e) {
              // When validation fails or other local issues
              echo 'Facebook SDK returned an error: ' . $e->getMessage();
            exit;
        }

        if (isset($accessToken)) {
              // Logged in!
              $_SESSION['facebook_access_token'] = (string) $accessToken;

              // Now you can redirect to another page and use the
              // access token from $_SESSION['facebook_access_token']

              echo "Finally logged in! Token:$accessToken";
        }           
    } else {
        // Well looks like we are a fresh dude, login to Facebook!
        $helper = $fb->getRedirectLoginHelper();
        $permissions = ['email', 'user_likes']; // optional
        $loginUrl = $helper->getLoginUrl('http://mywebsite.com/myapp/index.php', $permissions);

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

}

exit;
Éric Senterre
  • 161
  • 1
  • 1
  • 8

23 Answers23

30

I had to add this lines in some servers:

$helper = $fb->getRedirectLoginHelper();
if (isset($_GET['state'])) {
    $helper->getPersistentDataHandler()->set('state', $_GET['state']);
}

I get this error randomly, depending of the server config.

mdalda
  • 317
  • 3
  • 4
9

If you're using a "www" version of your site to generate the login link and you get redirected to the non-www version, you'll run into issues with your session. So make sure you access the www version of your site and then define the callback url to the same www version. Also, you can define permanent redirects in your server configuration to make sure anyone accessing your site from the non-www version gets redirected to the www version or vice versa.

Cedric Ipkiss
  • 5,662
  • 2
  • 43
  • 72
9

session_start() at the beginning of both the scripts. I got solution from here: https://github.com/facebook/facebook-php-sdk-v4/issues/473

Ruhul Amin
  • 1,751
  • 15
  • 18
3

I also ran into the same problem and after researching on stackoverflow putting line

$_SESSION['FBRLH_state']=$_GET['state']; 

above has solved my problem

$helper = $fb->getRedirectLoginHelper();  
MMSA
  • 810
  • 8
  • 22
ashutosh
  • 81
  • 7
2

you receive this error if you origin hostname is different than the target hostname once authenticated.

$loginUrl = $helper->getLoginUrl('http://mywebsite.com/myapp/index.php', $permissions);

with this statement, if the visitor on your website used http://www.mywebsite.com/ the cross-site error will be raised.

You must ensure that origin and target hostname are exactly the same, including the eventual www prefix.

Fixed version:

$loginUrl = $helper->getLoginUrl('http://'.$_SERVER['SERVER_NAME'].'/myapp/index.php', $permissions);
Jean.R
  • 466
  • 3
  • 10
2

Laravel 5.2

I have this error too "Cross-site request forgery validation failed. Required param “state” missing".

and after reading this for hours. I tried to change the vendor script.

in vendor\facebook\php-sdk-v4\src\Facebook\Helpers\FacebookRedirectLoginHelper.php on line 123, I change this script:

private function makeUrl($redirectUrl, array $scope, array $params = [], $separator = '&')
{
    $state = $this->pseudoRandomStringGenerator->getPseudoRandomString(static::CSRF_LENGTH);
    $this->persistentDataHandler->set('state', $state);

    return $this->oAuth2Client->getAuthorizationUrl($redirectUrl, $state, $scope, $params, $separator);
}

into (I add Session::put('state', $state);)

private function makeUrl($redirectUrl, array $scope, array $params = [], $separator = '&')
{
    $state = $this->pseudoRandomStringGenerator->getPseudoRandomString(static::CSRF_LENGTH);
    $this->persistentDataHandler->set('state', $state);
    Session::put('state', $state);
    return $this->oAuth2Client->getAuthorizationUrl($redirectUrl, $state, $scope, $params, $separator);
}

and on line 234, I change this script:

protected function validateCsrf()
{
    $state = $this->getState();
    $savedState = $this->persistentDataHandler->get('state');

    if (!$state || !$savedState) {
        throw new FacebookSDKException('Cross-site request forgery validation failed. Required param "state" missing.');
    }

    $savedLen = strlen($savedState);
    $givenLen = strlen($state);

    if ($savedLen !== $givenLen) {
        throw new FacebookSDKException('Cross-site request forgery validation failed. The "state" param from the URL and session do not match.');
    }

    $result = 0;
    for ($i = 0; $i < $savedLen; $i++) {
        $result |= ord($state[$i]) ^ ord($savedState[$i]);
    }

    if ($result !== 0) {
        throw new FacebookSDKException('Cross-site request forgery validation failed. The "state" param from the URL and session do not match.');
    }
}

into (I added $this->persistentDataHandler->set('state', Session::get('state'));)

protected function validateCsrf()
{
    $state = $this->getState();
    $this->persistentDataHandler->set('state', Session::get('state'));
    $savedState = $this->persistentDataHandler->get('state');

    if (!$state || !$savedState) {
        throw new FacebookSDKException('Cross-site request forgery validation failed. Required param "state" missing.');
    }

    $savedLen = strlen($savedState);
    $givenLen = strlen($state);

    if ($savedLen !== $givenLen) {
        throw new FacebookSDKException('Cross-site request forgery validation failed. The "state" param from the URL and session do not match.');
    }

    $result = 0;
    for ($i = 0; $i < $savedLen; $i++) {
        $result |= ord($state[$i]) ^ ord($savedState[$i]);
    }

    if ($result !== 0) {
        throw new FacebookSDKException('Cross-site request forgery validation failed. The "state" param from the URL and session do not match.');
    }
}

that is all what I did. and the error gone.

2

Setting [PersistentDataHandler] explicitly before you get your tokens will guarantee that the request will be a success. Below shows how to fetch $_GET['state'] & simply inject it into the "to be used [helper]" on Symfony 2.x | 3.x

I had the exact same issue as the O.P. and this fixed my problem.

//create your new facebook sdk instance
$this->fb = new Facebook([
   'app_id' => $facebookAppId,
   'app_secret' =>$facebookAppSecret,
   'default_graph_version' =>$facebookDefaultGraphVersion
]);    

//retrieve the helper
$this->helper = $this->fb->getRedirectLoginHelper();

//below is the money shot
//setting this explicitly before you get your tokens will guarantee that the request will be a success. It Fetches $_GET['state'] & simply injects it into the "to be used [helper]"
$this->helper->getPersistentDataHandler()->set('state', $request->query->get('state'));
Dan Baddeley
  • 87
  • 1
  • 10
2

This is a common issue that many people facing in FB Api. this is only a SESSION problem. To solve this issue add some code like.

On callback script usually fb-callback.php add "session_start();" just before you include the facebook autoload file. and then "$_SESSION['FBRLH_state']=$_GET['state'];" after the "$helper = $fb->getRedirectLoginHelper();" line.

Example :

<?php 
session_start(); /*Add session start*/
include 'vendor/autoload.php';
include 'config.php'; /*Facebook Config*/
$helper = $fb->getRedirectLoginHelper();
$_SESSION['FBRLH_state']=$_GET['state']; /*Add This*/
try {
  $accessToken = $helper->getAccessToken();
} ?>
Ricky Harerri
  • 341
  • 2
  • 4
  • 1
    Be aware that such solution breaks CSRF protection. Because protection based on comparison of stored token and received one. Provided code force them to match - its overwrite stored token with received. – 3DFace Jul 27 '17 at 10:49
1

The real problem here was the encoding of my PHP file. I used UTF8, but it should have been UTF8 w/o BOM.

The side effect of the wrong encoding was that my SESSION was not working properly, and then the SDK wasn't able to retrieve the necessary informations to work properly.

A properly configured error reporting would have told that there was an issue straight way.

I think that we can fill this bug in the "noob" category. But still, I think it can be useful to other noobs like me.

Éric Senterre
  • 161
  • 1
  • 1
  • 8
  • Hi Éric, I have the same error. Bit of a noob myself. I swear it was working fine a few days ago but now I have the same error as yourself. My provider is running UTF-8 (default_charset)...are you saying this needs to change to UTF8 w/o BOM for the login to work? Thanks – Tatters Jul 21 '15 at 19:48
  • Yes your PHP file needs an encoding of UTF8 w/o BOM. Otherwise the PHP file send a character before "session_start()" can create his cookies in the HTTP header (or something like that). I get in this problem in part because I'm a new user of Notepad++. And I blindly set my encoding to UTF8 because French accents weren't showing correctly on my page, like I used to do in EditPlus. So basically, YES set your PHP file encoding to UTF8 w/o BOM. I can't guarantee that it'll fix your problem, but it solved mine. – Éric Senterre Jul 21 '15 at 20:11
  • Nice one, thanks. I'll give it a try and see what happens. – Tatters Jul 21 '15 at 20:28
1

For me the problem is solved now just by starting the session by adding this:

session_start();

at the beginning of both files (the first file generating facebook url and the callback file: login.php and fb-callback.php (https://developers.facebook.com/docs/php/howto/example_facebook_login)).

I also had to add this:

$config['app_id'] = 'myapp_id';

at the top of to prevent another non related error.

Adam Tong
  • 571
  • 1
  • 4
  • 16
1

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

you need to make sure your native PHP session feature is properly set.

Community
  • 1
  • 1
Chao Chen
  • 51
  • 1
1

I ran into similar problem and found Nanang Koesharwanto's solution. It's more like quirk as modifying source files in vendor directory is a very bad idea. So here's the trick.

public function callback(Request $request)
{
    $this->helper->getPersistentDataHandler()->set('state', $request->state);
    return $this->helper->getAccessToken();
}

If it fails put use Session; in your controller.

Rajender Joshi
  • 4,155
  • 1
  • 23
  • 39
1

For me this was happening due to 'persistent_data_handler' . By adding this in Facebook config, I was able to make it work.

session_start();
$fb = new Facebook\Facebook([
  'app_id'     => '6XXXXXXXXX',
  'app_secret' => '5XXXXXXXXXXXXXX',
  'default_graph_version' => 'v2.6', 
  'persistent_data_handler' => 'session'
  ]);
Amit Tyagi
  • 291
  • 2
  • 6
1

I was trying to implement Facebook login in Symfony with Facebook PHP SDK and I had the same error "Cross-site request forgery validation failed. Required param “state” missing".

I solved the problem by adding persistent_data_handler parameter to my facebook app instanciation with a custom handler which implements the PersistentDataInterface of Facebook PHP SDK.

Works like a charm.

public function facebookCallbackAction(Request $request) {
    $session = $request->getSession();

    $fb = new \Facebook\Facebook([
        'app_id' => $this->container->getParameter('facebook_app_id'),
        'app_secret' => $this->container->getParameter('facebook_app_secret'),
        'default_graph_version' => 'v2.5',
        'persistent_data_handler' => new SymfonyPersistentDataHandler($session),
    ]);
}

My custom handler :

use Facebook\PersistentData\PersistentDataInterface;
use Symfony\Component\HttpFoundation\Session\Session;

class SymfonyPersistentDataHandler implements PersistentDataInterface {
    protected $session;
    protected $sessionPrefix = 'FBRLH_';

    public function __construct(Session $session) {
        $this->session = $session;
    }

    public function get($key) {
        return $this->session->get($this->sessionPrefix . $key);
    }

    public function set($key, $value) {
        $this->session->set($this->sessionPrefix . $key, $value);
    }
}
Julien F.
  • 645
  • 4
  • 7
0

Finally, looking into FB code, I discovered that the problem "Cross-site request forgery validation failed. Required param “state” missing" and similars are caused by PHP variable $_SESSION['FBRLH_state'] that for some "strange" reason when FB call the login-callback file.

To solve it I store this variable "FBRLH_state" AFTER the call of function $helper->getLoginUrl(...). Is very important to do only after the call of this function due to is inside this function when the variable $_SESSION['FBRLH_state'] is populated.

Below an example of my code in the login.php:

$uri=$helper->getLoginUrl($uri, $permissions);
foreach ($_SESSION as $k=>$v) {                    
    if(strpos($k, "FBRLH_")!==FALSE) {
        if(!setcookie($k, $v)) {
            //what??
        } else {
            $_COOKIE[$k]=$v;
        }
    }
}
var_dump($_COOKIE);

And in the login-callback.php before calling all FB code:

foreach ($_COOKIE as $k=>$v) {
    if(strpos($k, "FBRLH_")!==FALSE) {
        $_SESSION[$k]=$v;
    }
}

Last, but not least, remember also to include code for PHP session so..

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

I hope this response can help you to save 8-10 hours of work :) Bye, Alex.

ale500
  • 270
  • 2
  • 10
0

you have to make sure that the session start before the script runs. but again it will throw an 443: Network is unreachable if you start a session again on the same script. hope this helps some one.

I just used if (session_status() == PHP_SESSION_NONE){ session_start(); }

Tharindu
  • 640
  • 6
  • 17
0

To those of you of you who use cakephp 3.x and have this problem and you have no clue how to solve it. Add session_start(); at the beginning of your auth and callback method.

public function Facebookauth()
{
session_start();
 $fb = new Facebook([
      'app_id' => '{app_id}',
      'app_secret' => '{app_secret}',
      'default_graph_version' => 'v2.6',
     ..........

    ]);

you can still use 
     $session = $this->request->session();
0

The fix for me was to change 'secure' => true to false in config/session.php. I had accidently set this to true while not using https in the first place.

0

For me the solution was catching the exception by replacing the namespaced Facebook\Exceptions\FacebookSDKException to just Exception, because the script already used it.

use Facebook\Facebook;

// code ...

$fb = new Facebook([
    'app_id' => FB_APP_ID,
    'app_secret' => FB_APP_SECRET,
    'default_graph_version' => 'v2.5',
]);

$helper = $fb->getRedirectLoginHelper();
try {
    $accessToken = $helper->getAccessToken();
} catch (Exception $e) {
    echo $e->getMessage();
    exit;
}
michalzuber
  • 5,079
  • 2
  • 28
  • 29
0

If the session_start() does not still solve the problem, you put a wrong URI in the Valid OAuth Redirect URI of your Facebook developer app.

To solve: First, go to your fb-callback.php, find the URI you put in the header function, example: header("Location: https://localhost/home.php"). Then go to your Facebook developer apps and then in the sidebar, click Facebook Login which is under the Products tab then click Settings. In the Valid OAuth Redirect URIs, add the URI you put in the header. From my example, I will put https://localhost/home.php.

Hope this helps.

0

I resolved this error by setting my call back url in the "Facebook -> Login -> Valid OAuth Redirect URIs" in facebook's developer account.

-1

Finally after all this nice errors , i fixed all my problems with another solution , the example from facebook developer docs are outdated .

SDK V5 and APi 2.4 works like in this tutorial described , the access token need to be defined

Be sure you fill APP-IP|APP-SECRET with your credentials .

Example from tutorial :

$fb = new Facebook\Facebook([
      'app_id' => 'APP-ID',
      'app_secret' => 'APP-SECRET',
      'default_graph_version' => 'v2.4',
      'default_access_token' => 'APP-ID|APP-SECRET'
]);

Use this and not the example on developer facebook docs .

Have fun :)

RakonDark
  • 33
  • 5
-1

Whatever you do. Just don't call getAccessToken() more than once. It removes the state from session as soon as it is called.

Jasmeet Singh
  • 499
  • 5
  • 6