Laravel Socialite: InvalidStateException in AbstractProvider.php line 199: - facebook

For some reason I get this exception when I try to log in with Laravel Socialite with either Facebook or Google:
InvalidStateException in AbstractProvider.php line 199:
The exception are thrown from my SocialiteController, when it tries to get the user from the facebook driver.
public function callback(SocialAccountService $service, $provider)
{
try {
var_dump(Socialite::driver('facebook')->user());
Here are the part of AbstractProvider.php that seem to throw the actual exception:
public function user()
{
if ($this->hasInvalidState()) {
throw new InvalidStateException;
}
I have been following this tutorial https://blog.damirmiladinov.com/laravel/laravel-5.2-socialite-twitter-login.html#.WFK0BfnhCUk. The login have worked fine until last week, for some reason.
I read other articles saying I should change config/session.php so domain is not null but my current domain (in my case localhost:8000 since I run local with XAMPP), and refresh Laravel cache etc. But it did not work.

I have discovered that my Laravel application cookies where missing, which caused the InvalidStateException exception.
I also noticed that the cookies were not recreated after each HTTP request. When I changed the domain value in Config/Session.php from my current one ("localhost") to null (the default value), then the cookies were recreated again.

Try
session()->put('state', $request->input('state'));
$user = Socialite::driver('facebook')->user();

Related

Laravel 5.1 - Facebook Authentication through Socialite

I am trying to implement Facebook Login to my project however I receive an error:
ClientException in Middleware.php line 69:
Client error: 400
in /Applications/MAMP/htdocs/laratest/vendor/guzzlehttp/guzzle/src/Middleware.php line 69
at Middleware::GuzzleHttp{closure}(object(Response)) in Promise.php line 199
at Promise::callHandler('1', object(Response), array(object(Promise), object(Closure), null)) in Promise.php line 152
at Promise::GuzzleHttp\Promise{closure}() in TaskQueue.php line 60
at TaskQueue->run() in CurlMultiHandler.php line 96
Steps I've gone through:
composer require laravel/socialite & composer update.
In my config>services.app,
'facebook' => [
'client_id' => env('FB_CLIENT_ID'),
'client_secret' => env('FB_SECRET_ID'),
'redirect' => 'http://localhost.com:8888',
],
Added
Laravel\Socialite\SocialiteServiceProvider::class, & 'Socialite' => Laravel\Socialite\Facades\Socialite::class, in config>app.php
Set up the routes successfully.
Route::get('auth/facebook', 'Auth\AuthController#redirectToProvider');
Route::get('auth/facebook', 'Auth\AuthController#handleProviderCallback');
Setup the link successfully in my blade file. Login with Facebook
In my Controller>AuthController.php, I added:
use Laravel\Socialite\Facades\Socialite;
** Beside everything that AuthController has, inside the AuthController class, I added:**
public function redirectToProvider()
{
return Socialite::driver('facebook')
->scopes(['scope1', 'scope2'])->redirect();
}
/**
* Obtain the user information from Facebook.
*
* #return Response
*/
public function handleProviderCallback()
{
$user = Socialite::driver('facebook')->user();
// $user->token;
}
Also users table:
public function up()
{
Schema::create('users', function (Blueprint $table) {
$table->increments('id');
$table->string('username');
$table->string('name');
$table->string('email')->unique();
$table->string('password', 60);
$table->string('avatar');
$table->string('provider');
$table->string('provider_id');
$table->rememberToken();
$table->timestamps();
});
}
Edit:
When I comment out $user = Socialite::driver('facebook')->user(); part, I get redirected to localhost.com/auth/facebook
Edit 2:
My .env file:
'facebook' => [
FB_CLIENT_ID => '###',
FB_SECRET_ID => 'this-is-secret!',
],
I think your two routes should not have the same URL, because Laravel has no way to know which to use.
Facebook returns a ?code parameter, so make use of it! If ?code is included, then handle the login logic from Socialite, if not, redirect to Facebook.
You have two unknown scopes in your code, scope1 and scope2, those are unknown to Facebook and will return an error when trying to access them. Use real scopes like public_profile or email and you should be set. (scopes are the permissions you are requesting)
Make sure Laravel is calling properly your config entries with
php artisan config:clear
And also check your .env file for FB_CLIENT_ID and FB_SECRET_ID entries. Probably client's failing because it's trying to connect to facebook without client and secret, falling into an exception.
I've had the same problem(ClientException in Middleware.php line 69: Client error: 400).
I just switch off "Require App Secret " in the facebook's advanced settings and this solved the problem. Maybe this information will be helpful.
I had a similar problem with Facebook APi - it gave Response code 400
and Ive just tried:
composer require laravel/socialite & composer update
and it updated dependencis:
Updating dependencies (including require-dev)
- Removing laravel/socialite (v2.0.11)
- Installing laravel/socialite (v2.0.14)
Im Using Laravel 5.1:
composer.json:
"laravel/framework": "5.1.*",
"laravel/socialite": "^2.0"
And it seems to work fine now
Hope this helps someone

The "state" param from the URL and session do not match

In facebook documantion
require('include/facebook/autoload.php'); //SDK directory
$fb = new Facebook\Facebook([
'app_id' => '***********',
'app_secret' => '***********************'
]);
$helper = $fb->getRedirectLoginHelper();
$permissions = ['email', 'public_profile']; // optional
$loginUrl = $helper->getLoginUrl('http://www.meusite.com.br/login-callback.php', $permissions);
When direct it to the url $loginUrl, the return is:
Facebook SDK returned an error: Cross-site request forgery validation failed. The "state" param from the URL and session do not match
I had the same error.
The problem occurred because I did getLoginUrl(...) before getAccessToken()
So rid of getLoginUrl(...) in redirected URL and code should works.
I had the same issue and for me that error was occurring because I did not put session_start(); in my login.php page code before calling getLoginUrl(..) and also at the top of login-callback.php page.
Just put session_start(); in your "login" page and "login-callback" page and it will work surely just like it is working for me now.
There could be 2 reason for this error:
you didn't call session_start(); before getLoginUrl call
You executed getLoginUrl again in login-callback.php, so state value regenerated and mismatched with the redirected value
Possible Fixes : I used the following configuration settings .
Enable WebAuthLogin under the advanced tab . Provide the url in the WebAuthLogin settins as same as that you provide in $loginUrl ;
For example if you use $loginUrl as https://example.com/ use that same in the WebAuthlogin Url
$loginUrl = $helper->getLoginUrl('https://example.com/', $permissions);
This problem occures also in case that you generate 2 or more login links on the same page (e.g. one for login and other for registration - even both point to the same url, they have just different labels).
Facebook SDK creates/updates $_SESSION[FBRLH_state] for each new generated loginURL. So if there are 2 generated URLs (using $helper->getLoginUrl()) then the $_SESSION[FBRLH_state] is 2-times rewritten and valid only for the last generated URL. Previous login URL becomes invalid. It means that it is not possible to generate 2 valid loginURLs. In case that 2 same URLs are generated then return the first one and avoid call of Facebook SDK for generation of second one.
I had the same problem.
The reason for this error is because --->
When "$helper->getLoginUrl" calls, it create a session variable "FB_State", and this is something to FB uses to match the token. Every-time getLoginUrl calls, it create new state. Then after user authorized and redirect back, if you codes cannot detect this event and re-run "$helper->getLoginUrl", then this error will occur.
The solution ->
refine your coding, stop run "$helper->getLoginUrl" again if authorized.
if you already rerun, then set the session variable for the token to NULL if you have, then User can re-authorize again.
when user tries re-authorize, they can remove the authorized APP once or you need to generate new link with "$helper->getReRequestUrl"
Yet, token has be called by "getAccessToken()" before the "$helper->getLoginUrl" or "$helper->getReRequestUrl" runs.
Good Luck!!!!!
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.
This issue was a bit confusing for me, because I had to change a line at the facebook src file:
src/Facebook/Helpers/FacebookRedirectLoginHelper.php
at the function: "validateCsrf" like this:
if ($result !== 0) {
throw new FacebookSDKException('Cross-site request forgery validation failed. The "state" param from the URL and session do not match.');
}
And change it into:
if ($result === 0) {
throw new FacebookSDKException('Cross-site request forgery validation failed. The "state" param from the URL and session do not match.');
}
I don't know if this makes a violation to the facebook SDK security, so I truly opened to any exlanation or recommendation for this answer.
You may also make the following changes at the facebook app manager:
add your site and callback-url into your facebook app account at:
setting->advanced:Valid OAuth redirect URIs
Don't forget to add another url with slash (/) at the end of each url and check all 4 checkboxes at Client OAuth Settings.
I had the same error. Are you using 1 file or 2? I was trying to get by using 1 file but my error was resolved when I split into login.php & fb-callback.php as the documentation recommended. My sessions were being re-written so the state was never saved properly.
Good luck!
Happens when the session in missing a needed variable.
might be caused by several things.
In my case I left the "www" out of the callback URL
You could actually be parsing the data from another domain... for example:
website.com is different from www .website.com
If you're parsing data from http ://website.com/login.php to http://www.website.com/fb-callback.php this would be a cross-domain problem and the error you are receiving would be because of that....
http ://website.com and http ://www.website.com are the same but the script identifies them as different..... hope that gives insight to the problem.

passport.socketio's passport "Failed to deserialize user out of session". But passport in my main app (with the same key) deserializes just fine

passport.socketio throwing this error, while failing to authorize the user.
Error: Error: Failed to deserialize user out of session
I've narrowed the problem down to passport.socketio's /lib/index.js.
At line 59
auth.passport.deserializeUser(userKey, function(err, user) {
if (err)
return auth.fail(data, err, true, accept);
...
It throws that error. Debugger tells me the userKey is valid, and should deserialize the user. It's the same key that passport in my main app uses to deserialize the user. (it's the ID of mongoDB object). And passport in my main app has no problem deserializing the user. (details)
So don't know why this still throws the error.
The userKey passed here is the same key passport in my main app uses to deserialize.
I've gone to the extent of making the userKey global and putting it in my main code
passport.deserializeUser(global.userKey, function(err, user) {
if (err)
return auth.fail(data, err, true, accept);
console.log('ok');
Which results in infinite loop (because it's inside outer passport.deserialize) but iut prints 'ok'!, so passport from my main app can atleast deserialize just fine using the same thing that passport from index.js (passport.socketio\lib\index.js) can not! .. for some reason.
Then I've even tried passing the passport object itself from main app
io.set('authorization', require('passport.socketio').authorize({
passport: passport,
...
which actually results in no errors!! but then I don't get the socket.handshake object.
I'm out of ideas to diagnose this any further and would really appreciate any help whatsoever.
What could be causing passport.socketio's passport to not "deserialize user out of session"?
Deleted npm_modules, re-wrote the packages.json with "every_package":"latest", and so basically re-installed every package's latest version. That fixed it.
One problem could be that you have configured your 'passport' instance in the main app to use a specific 'deserializeUser' implementation. look for all the places where your passport has been intiallized in the main app. (If its a framework like mean.io, you will find it in config/passport.js).
Make sure the same initiallization is done to the passport instance in the socket app. Pass it to passportsocketio as such:
passportSocketIo.authorize({
passport: passport,
cookieParser: express.cookieParser,
key: 'connect.sid'
...
});

CakePHP 2.0 send eMail error

I'm using the SignMeUp Plugin for user registration in CakePHP 2.0 (whose homepage seems to be down atm).
For the most part, everything works perfectly fine, except that I'm getting the following seemingly unrelated error whenever a function uses $this->Email->send() :
Trying to get property of non-object
[CORE\Cake\View\Helper\PaginatorHelper.php, line 111]
The line is:
public function beforeRender($viewFile) {
$this->options['url'] = array_merge($this->request->params['pass'],
$this->request->params['named']);
Not sure how that is actually related to the email, so I'm at a loss as to what can possibly cause this error, as the emails are actually being sent no problem.
An example function in the plugin would be:
protected function __sendActivationEmail($userData) {
$this->__setUpEmailParams($userData);
$this->__parseEmailSubject('activation', $userData);
if ($this->__setTemplate(Configure::read('SignMeUp.activation_template'))) {
if ($this->Email->send()) {
return true;
}
}
}
I personally see nothing wrong with this either... Not sure what else could possibly be causing this error. If someone of you has any kind of idea, that would really be appreciated!
BTW: If I set debug to 0, I get a blank page after the function executes instead of a proper redirect, so that's not a solution.
I had the same problem because I included the paginator as helper:
public $helpers = array('Time','Paginator');
In CakePHP 2.0 the Paginator is always included and for some reason there seems to be a conflict with the sendMail if you include the helper in the controller. So if you delete the Paginator from the helpers list it should work without error.

Zend_Auth clearidentity and Zend_Session::destroy causing confusion

I have the following logout action:
public function logoutAction() {
Zend_Auth::getInstance()->clearIdentity();
Zend_Session::destroy();
$this->_helper->flashMessenger->addMessage(array('success' =>
_('You were successfully logged out.')));
$this->_redirect('/index/index');
}
If I don't comment out the line: Zend_Session::destroy() I get an error:
Fatal error: Uncaught exception 'Zend_Session_Exception' with message 'The session was explicitly destroyed during this request, attempting to re-start is not allowed.' in /usr/local/share/php/library/Zend/Controller/Plugin/Broker.php on line 336 Zend_Session_Exception: The session was explicitly destroyed during this request, attempting to re-start is not allowed.
I have read about this issue here and here but remain unclear on how I should proceed. Should I just not use Zend_Session::destroy()? What would be the implications and dangers of not using it, and what is the alternative?
What causes you problems is, that right after destroying the session, you are reusing it (by facilitating the FlashMessenger. If not destroying the session after logout bothers you, you could display a logout page instead of redirecting to your frontpage with a flash message.
Leaving some of your session data intact after your user logged out, might have security implications, but that depends on what you store in your session and where and how you use the data. In order to make sure, that you don't keep data, that belonged to the logged in user in your session, just use a specific session namespace for this data and call unsetNamespace() upon logout.
Zend_Auth have its own session namespace and after Zend_Auth::getInstance()->clearIdentity(); it removes it so there is no need to destroy all session nemaspaces if you use them.
Example what here happens:
// logging user
$_SESSION['Zend_Auth'] = 'logged user data';
// after Zend_Auth::getInstance()->clearIdentity();
$_SESSION['Zend_Auth'] = null;
// after Zend_Session::destroy();
session_destroy();