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

后端 未结 25 889
南方客
南方客 2020-12-01 01:37

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

$fb = new Facebook\\Facebook([
    \'app_id\' => \'11111111111\',
    \'app_secret\' =>         


        
相关标签:
25条回答
  • 2020-12-01 02:25

    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.

    0 讨论(0)
  • 2020-12-01 02:26

    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.

    0 讨论(0)
  • 2020-12-01 02:27

    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

    0 讨论(0)
  • 2020-12-01 02:27

    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);
    
    0 讨论(0)
  • 2020-12-01 02:27

    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.

    0 讨论(0)
  • 2020-12-01 02:30

    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!

    0 讨论(0)
提交回复
热议问题