CSRF state token does not match one provided FB PHP SDK 3.1.1 Oauth 2.0
My server logs show a "CSRF state token does not match one provided" error which seems to happen for almost every user. However, the users are created and/or authenticated and I am able to retrieve the user info. I am using a Linux server with Apache. I am also using the latest Facebook PHP SDK v.3.1.1 Can anyone tell me why this is happening and how to fix it?
Solution 1:
I had a similar issue last week, and tracked it down to the state
field being overwritten by multiple calls to getLoginUrl()
. Each time you call getLoginUrl()
, a new state
token is generated in the SDK and stored in the $_SESSION
(it's just a random value), so if you call it twice and the user uses the first link to log in, the second call will have reset the SDK's internal state
token, and you will get this error in your logs.
The SDK looks for the same state
token in the URL coming back after Facebook authorizes the user and redirects them back to your site, and if it doesn't match it will log this error (here's a link to the source).
Solution 2:
Facebook SDK code has a bug when checking against tokens twice in the same handler.
I edited the getCode function of facebook.php
like this:
protected function getCode() {
if (!isset($_REQUEST['code']) || !isset($_REQUEST['state']) || $this->state === null) {
return false;
}
if ($this->state === $_REQUEST['state']) {
// CSRF state has done its job, so clear it
$this->state = null;
$this->clearPersistentData('state');
return $_REQUEST['code'];
}
self::errorLog('CSRF state token does not match one provided.');
return false;
}
to be more clear and does not state invalid token if called twice.
To be clear the function can be called twice on the same url handler if eg:
$facebook->getUser();
and then in the same handler $facebook->getLogoutUrl()
then the getCode()
is called twice thus resulting into and invalid error message
Solution 3:
Well, I've encountered this exact problem once, and I had a problem with the state
& code
parameters in the URL - my .htaccess
file was not forwarding them.
I'm guessing you are having the same problem.
CSRF state token does not match one provided
Hope this helps
Solution 4:
To add a bit to chesles's answer, this problem can occur if you're playing with the session_start() - session_write_close() functions, as I did.
If there is no started session when you're requesting the loginUrl, you'll get this error.
Sidenote: Why bother stopping the session?
Scripts that use sessions stops each other, because they're waiting for the session array to be available to use.
Imagine that you have a popular application, with thousands of users, and have an action (a php script) where you post a picture. Something like this:
--starting session at the top of the script
--connecting to facebook
--creating the image
--sharing the image with the api call
--script end, session closes automatically
Doing this, the session will be used by the script for a long time for no reason. Be careful with such scripts, use something like this instead:
--starting session right before where you create the facebook object
--connecting to facebook
--closing session with session_write_close(), the session array's available, other scripts can load
--creating the image
--sharing the image with the api call /* It think this doesn't need a session. */
--script end, session already closed manually.
Cheers.
Solution 5:
One additional note - although it is not stated in the Facebook PHP API documentation, you have to have apache configured for PHP sessions for the login process to work. That turned out to be the problem we encountered when we were getting the "CSRF state token does not match one provided".
Make sure if you are using a server pool that you have it set up to use memcache for session information, otherwise apache will write the session information locally and if the next request doesn't go to the same server you will get the "CSRF state token does not match one provided".
This was one of those things that worked like a charm in a development environment (with one server) but failed in production.
We also had to reconfigure our CDN settings to make sure we were passing through the PHP Session cookie.