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

PhpWordpressFacebookFacebook Graph-Api

Php Problem Overview


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

$fb = new Facebook\Facebook([
    '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 (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)) {
        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 (Facebook\Exceptions\FacebookSDKException $e) {
            echo "<p>Error getting long-lived access token: " . $helper->getMessage() . "</p>\n\n";
            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.

Php Solutions


Solution 1 - Php

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 Facebook\Facebook([
    '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 Facebook\Facebook([
    '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.

Solution 2 - Php

insert this code after $helper = $fb->getRedirectLoginHelper();

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

Solution 3 - Php

Lots of great answers already mentioned, here is the one which helped for me,

I found that the problem is Cross-site request forgery validation failed. Required param “state” missing in FB code and here is the solution

After this line

$helper = $fb->getRedirectLoginHelper();

Add the below code,

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

Solution 4 - Php

I got this error while using the Facebook SDK in Symfony2, writing a Twig Extension to display data from the API in templates.

The solution for me was adding 'persistent_data_handler'=>'session' to the Facebook object config, which causes the state data to be stored in a session key instead of memory:

$fb = new Facebook\Facebook([
    'app_id' => 'APP_ID',
    'app_secret' => 'APP_SECRET',
    'default_graph_version' => 'v2.4',
    'persistent_data_handler'=>'session'
]);

By default, it was using the built-in memory handler, which didn't work properly for me. Maybe because some functions are being called from within a Twig Extension, as the memory handler does work when using the SDK exclusively in normal controllers/services.

Apparently the state is set when you call getLoginUrl(), and is retrieved anytime you call getAccessToken(). If the saved state returns null (because your data handler isn't as persistent as it should be), the CSRF validation check fails.

If you need to treat sessions in a particular way or you want to store the state somewhere else, you can also write your own handler with 'persistent_data_handler' => new MyPersistentDataHandler(), using the FacebookSessionPersistentDataHandler as an example.

Solution 5 - Php

This happens when the Facebook library cannot match up the state param it receives back from Facebook with the one that it sets, by default, in the session. If you are using a framework such as Laravel, Yii2, or Kohana that implements its own session storage the standard Facebook session implementation will likely not work.

To fix it you need to create your own implementation of the PersistentDataInterface using your framework's session library and pass this to the Facebook\Facebook constructor.

Here's an example of a Laravel persistence handler from Facebook:

use Facebook\PersistentData\PersistentDataInterface;

class MyLaravelPersistentDataHandler implements PersistentDataInterface
{
  /**
   * @var string Prefix to use for session variables.
   */
  protected $sessionPrefix = 'FBRLH_';

  /**
   * @inheritdoc
   */
  public function get($key)
  {
    return \Session::get($this->sessionPrefix . $key);
  }

  /**
   * @inheritdoc
   */
  public function set($key, $value)
  {
    \Session::put($this->sessionPrefix . $key, $value);
  }
}

Example constructor params:

$fb = new Facebook\Facebook([
  // . . .
  'persistent_data_handler' => new MyLaravelPersistentDataHandler(),
  // . . .
 ]);

More info here: https://developers.facebook.com/docs/php/PersistentDataInterface/5.0.0

Solution 6 - Php

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.

Solution 7 - Php

For me, the problem was that I wasn't running a session before the script.

So, I added session_start(); before instantiating the Facebook class.

Solution 8 - Php

With Symfony, it doesn't work because the way session are managed.

To resolve the problem you can create a new handler wich work with symfony's session.

FacebookDataHandlerSymfony.php :

<?php

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

class FacebookDataHandlerSymfony implements PersistentDataInterface
{
   
    private $session;

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

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

    public function set($key, $value)
    {
        $this->session->set('FBRLH_' . $key, $value);
    }

}

And when you create the FB Object, you have just to specifie the new class :

$this->fb = new Facebook([
            'app_id' => '1234',
            'app_secret' => '1324',
            'default_graph_version' => 'v2.8',
            'persistent_data_handler' => new FacebookDataHandlerSymfony()
        ]);

Solution 9 - Php

The same issue occurred to me on laravel 5.4 i solved this issue by putting

session_start();

at the top of the script.

Below is the sample laravel controller namespace to give you a example how it will work.

<?php
namespace App\Http\Controllers;
session_start();
use Facebook\Facebook as Facebook;
?>

the issue is occurring because is has not yet started so by adding session start at the top of the script we are just starting the session.

hope it may help somebody..

Solution 10 - Php

The Facebook object has an instance variable called persistentDataHandler, usually an instance of FacebookSessionPersistentDataHandler, which has a set and a get method to access the native PHP sessions.

When generating the callback url using:

$loginUrl = $helper->getLoginUrl($callback_url, $permissions);

The methode FacebookRedirectLoginHelper->getLoginUrl() will create a 32-character random string, add it to the $loginUrl and save it to the code below using the persistentDataHandler's set method.

$_SESSION['FBRLH_' . 'state']

Later when the $helper->getAccessToken(); is called, the state param in the url will be compared to the stored in the same session to prevent CSRF. If not match, this will exception be thrown.

> FacebookSDKException: Cross-site request forgery validation failed. Required param "state" missing.

All these being said, you need to make sure your native PHP session feature is properly set during this process. You can check it, by adding

die($_SESSION['FBRLH_' . 'state']);

after

$loginUrl = $helper->getLoginUrl($callback_url, $permissions);

and before

$accessToken = $helper->getAccessToken();

to see if that 32-character string is there.

Hope it helps!

Solution 11 - Php

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

$loginUrl = $helper->getLoginUrl('http://MyWebSite', $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'], $permissions);

Solution 12 - Php

This might be kinda late but I hope it helps others since this problem still persists.

I had this problem for a while and I've searched around and have seen a lot of different solutions, many of which disable the CSRF check. So after everything I've read, this is what worked for me.

For what I understand, you get this error when your redirect URL doesn't match the one you have setup on your app settings so my issue was fixed every easily but I have also seen people have issues by not having their session started properly, so I will cover both issues.

Step 1: Ensure your session has started when it needs to.

for example: fb-config.php

session_start();
include_once 'path/to/Facebook/autoload.php';

$fb = new \Facebook\Facebook([
	'app_id' => 'your_app_id',
	'app_secret' => 'your_secret_app_id',
	'default_graph_version' => 'v2.10'
]);

$helper = $fb->getRedirectLoginHelper();

if your facebook callback code is on another file aside from the config, then start the session on that file too.

for example: fb-callback.php

session_start();
include_once 'path/to/fb-config.php';

try {
	$accessToken = $helper->getAccessToken();
} catch (\Facebook\Exceptions\FacebookResponseException $e) {
	echo "Response Exception: " . $e->getMessage();
	exit();
} catch (\Facebook\Exceptions\FacebookSDKException $e) {
	echo "SDK Exception: " . $e->getMessage();
	exit();
}

/** THE REST OF YOUR CALLBACK CODE **/

Now, what solved my actual issue.

Step 3: Set up your redirect URL in your app settings.

In your Facebook Login app settings, go to the Valid OAuth redirect URIs where you should have added the url that points to your fb-callback.php file.

http://example.com/fb-callback.php

AND ALSO

http://www.example.com/fb-callback.php

then setup your redirect url as follows.

$redirectURL = "http://".$_SERVER['SERVER_NAME']."/fb-callback.php";
$permissions = ['email'];
$fLoginURL = $helper->getLoginUrl($redirectURL, $permissions);

Why both with and without www and why use SERVER_NAME?

because your Valid OAuth redirect URI needs to match your redirect url in your code and if in you app settings you only set your OAuth redirect as http://example.com/fb-callback.php and set up your $redirectURL as http://example.com/fb-bacllback.php to make it match but the user entered your site as http://www.example.com then the user will get the Facebook SDK error: Cross-site request forgery validation failed. Required param “state” missing from persistent data because the URL the user is at, doesn't EXACTLY match what you have setup. Why? I have no freaking idea.

My approach makes it so if the user enters your site as http://example.com or http://www.example.com, it will always match what you setup in your app settings. why? because $_SERVER['SERVER_NAME'] will return the domain with or without the www depending on how the user entered the url in the browser.

This are my findings and this is about the only thing that worked for me without removing the CSRF check and so far, no issues.

I hope this helps.

Solution 13 - Php

you could just do this set the session with the new state

<?php
if(isset($_GET['state'])) {
      if($_SESSION['FBRLH_' . 'state']) {
	      $_SESSION['FBRLH_' . 'state'] = $_GET['state'];
      }
}
?>

Solution 14 - Php

In my case i have checked error and found error which lead me to solution with executing code:

date_default_timezone_set('Europe/Istanbul');

before script. Worked like a charm. For your location check: timezones.europe.php

Solution 15 - Php

Easy fix for me. I changed:

$loginUrl = $helper->getLoginUrl('http://www.MYWEBSITE.ca/index_callback.php', $permissions);

to:

$loginUrl = $helper->getLoginUrl('http://MYWEBSITE.ca/index_callback.php', $permissions);

removing the 'www' solved the problem.

Solution 16 - Php

The error is triggered if origin hostname is different than the target hostname [as Souch mentioned]. When visitors typed in URL address box as "http://website.com" that is different from "http://www.website.com";. We redirect them to the correct URL, by adding the following codes at topmost position before session_start().

  if($_SERVER['HTTP_HOST']!=='www.website.com'){
  header('location: http://www.website.com');
  }

Solution 17 - Php

Yii2 solution that works for me:

use Facebook\PersistentData\PersistentDataInterface;
use Yii;

class PersistentDataHandler implements PersistentDataInterface
{
    /**
     * @var string Prefix to use for session variables.
     */
    protected $sessionPrefix = 'FBRLH_';

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

    public function set($key, $value)
    {
        Yii::$app->session->set($this->sessionPrefix . $key, $value);
    }
}

Solution 18 - Php

I had the same error, because I forgot to add "www." to the sender address. In the Client-OAuth Settings there has to be the correct name.

Solution 19 - Php

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();
include 'vendor/autoload.php';
include 'config.php'; /*Facebook Config*/
$helper = $fb->getRedirectLoginHelper();
$_SESSION['FBRLH_state']=$_GET['state'];
try {
  $accessToken = $helper->getAccessToken();
} ?>

Solution 20 - Php

Might help someone, who is using Javascript Helper in frontend for authenticating the user and in PHP one is trying to to extract access_token from Redirect Login Helper. So use following

getJavaScriptHelper();

instead of

getRedirectLoginHelper();

Solution 21 - Php

SOLUTION FOR INTERMITTENT PROBLEMS

I was a) redirecting to Facebook login link, b) redirecting from login.php to main.php. Users would travel to main.php and a few other pages, then click back back back in browser.

Eventually, they would hit login.php with a bunch of creds posted to it, but Facebook removes the $_SESSION['FBRLH_state'] after a single success, so even though it had the proper $_GET['state'], it would error out.

The solution is to a) track internally if the user is logged in and avoid the repeat Facebook logic in login.php, OR b) keep track of all recently valid state parameters for that particular user (in a session perhaps) which were set by Facebook and if the $_GET['state'] is in that array, then do this:

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

In this case you can do this safely without breaking CSRF protection.

Solution 22 - Php

For me setting the session state worked

Complete code ( in the redirect url php )

$accessToken = '';

$helper = $fb->getRedirectLoginHelper();

if(isset($_GET['state'])){

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


try {
	$accessToken = $helper->getAccessToken();
} catch ( Facebook\Exceptions\FacebookResponseException $e ) {
	// When Graph returns an error
	echo 'Graph returned an error: ' . $e->getMessage();

}

Solution 23 - Php

I am playing around with Symfony and was having this problem one attempt yes and the next one no. I solved this problem by storing the facebookRedirectLoginHelper object into session, and retrieving it later on from the session instead of asking the Facebook object for it again.

The documentation (Symfony 4.3 at the time of this writing) states the following:

> Make sure your PHP session isn't already started before using the Session class. If you have a legacy session system that starts your session, see Legacy Sessions. > > Symfony sessions are designed to replace several native PHP functions. Applications should avoid using session_start(), session_regenerate_id(), session_id(), session_name(), and session_destroy() and instead use the APIs in the following section. > > While it is recommended to explicitly start a session, a session will actually start on demand, that is, if any session request is made to read/write session data.

So I think that retrieving the object from the session inherently starts the php session.

If you are using some php framework, keep that in mind.

Solution 24 - Php

For me the problem was different; (I was stupid) I had a popup window with Facebook login and again Facebook login buttons in Login/Signup page.

  • Whenever i click the Facebook button in the popup from other pages it worked fine.
  • But, when i click the Facebook button in the popup from Login/Signup page, it would not work.

The reason was i had re-instantiated the Facebook and getRedirectLoginHelper objects. I had to comment out these statements in Login/Signup pages, as it was already available.

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

is not the way to go. Although it worked.

Solution 25 - Php

if you keep to see the error, just clean your browser cache. When I've fixed the problem using the enobrev solution, I was continuing to experience the error. After a while I've understood that I needed to clean the cache and after I've restarted the browser it worked!

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionFadiView Question on Stackoverflow
Solution 1 - PhpenobrevView Answer on Stackoverflow
Solution 2 - PhpzratanView Answer on Stackoverflow
Solution 3 - PhpG.Ashok KumarView Answer on Stackoverflow
Solution 4 - PhpokdewitView Answer on Stackoverflow
Solution 5 - PhpGeorgeView Answer on Stackoverflow
Solution 6 - Phpale500View Answer on Stackoverflow
Solution 7 - PhpThe OninView Answer on Stackoverflow
Solution 8 - Phpuser4520510View Answer on Stackoverflow
Solution 9 - PhpusamaView Answer on Stackoverflow
Solution 10 - PhpChao ChenView Answer on Stackoverflow
Solution 11 - PhpJean.RView Answer on Stackoverflow
Solution 12 - PhpBlueSun3k1View Answer on Stackoverflow
Solution 13 - PhpJoshua Bigboy SpringmanView Answer on Stackoverflow
Solution 14 - PhpRoman LosevView Answer on Stackoverflow
Solution 15 - PhpMathieu WilsonView Answer on Stackoverflow
Solution 16 - PhpPiya PoonsawatView Answer on Stackoverflow
Solution 17 - PhpzibraView Answer on Stackoverflow
Solution 18 - PhpUmbrella BrothersView Answer on Stackoverflow
Solution 19 - PhpRicky HarerriView Answer on Stackoverflow
Solution 20 - PhpmakkiView Answer on Stackoverflow
Solution 21 - PhpPhyllis SutherlandView Answer on Stackoverflow
Solution 22 - PhpdigitalzoomstudioView Answer on Stackoverflow
Solution 23 - PhpdabichoView Answer on Stackoverflow
Solution 24 - PhpSuriyaView Answer on Stackoverflow
Solution 25 - PhpfancooloView Answer on Stackoverflow