Éric Senterre
Éric Senterre

Reputation: 161

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

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;

Upvotes: 13

Views: 37098

Answers (23)

Tarak Gandhi
Tarak Gandhi

Reputation: 1

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

Upvotes: 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.

Upvotes: 0

Cedric Ipkiss
Cedric Ipkiss

Reputation: 6347

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.

Upvotes: 9

Ricky Harerri
Ricky Harerri

Reputation: 361

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

Upvotes: 2

Jasmeet Singh
Jasmeet Singh

Reputation: 493

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

Upvotes: -1

Dan Baddeley
Dan Baddeley

Reputation: 87

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

Upvotes: 2

ashutosh
ashutosh

Reputation: 81

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

Upvotes: 3

Julien F.
Julien F.

Reputation: 643

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

Upvotes: 1

Rajender Joshi
Rajender Joshi

Reputation: 4205

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.

Upvotes: 1

michalzuber
michalzuber

Reputation: 5225

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

Upvotes: 0

mdalda
mdalda

Reputation: 327

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.

Upvotes: 31

Jonas Menk
Jonas Menk

Reputation: 1

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.

Upvotes: 0

Amit Tyagi
Amit Tyagi

Reputation: 320

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

Upvotes: 1

Emastmagy MastMagy
Emastmagy MastMagy

Reputation: 144

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

Upvotes: 0

Nanang Koesharwanto
Nanang Koesharwanto

Reputation: 97

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.

Upvotes: 2

Tharindu
Tharindu

Reputation: 650

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

Upvotes: 0

Ruhul Amin
Ruhul Amin

Reputation: 1775

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

Upvotes: 10

Chao Chen
Chao Chen

Reputation: 51

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.

Upvotes: 1

Jean.R
Jean.R

Reputation: 526

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

Upvotes: 2

Adam Tong
Adam Tong

Reputation: 571

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.

Upvotes: 1

RakonDark
RakonDark

Reputation: 33

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

Upvotes: -1

ale500
ale500

Reputation: 270

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.

Upvotes: 0

&#201;ric Senterre
&#201;ric Senterre

Reputation: 161

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.

Upvotes: 1

Related Questions