Facebook PHP SDK 5 :: API 2.4 :: Cross-site request forgery validation failed. Required param “state” missing

懵懂的女人 提交于 2019-11-28 21:34:13

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.

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

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.

ashutosh

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();  

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);

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'));

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();
} ?>

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.

Chao Chen

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.

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.

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.

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);
    }
}

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.

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.

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(); }

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();

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'
  ]);

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.

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;
}

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.

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 :)

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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!