Laravel Socialite: InvalidStateException

Resolved :

Socialite::driver('google')->stateless()->user()

I ran into this issue last night and solve it with the following solution.

More information on my issue, I've got

InvalidStateException in AbstractProvider.php line 182

in the function handleProviderCallback() when it re-direct back from Facebook login. It seems to be the same as your issue.

Furthermore I found my issue occurs when I open my site without www. When I open my site with www.mysite.com - no problem. At first I think my issue is random until I've got the clue by Chris Townsend's reply to the question - Thank you very much.

The Solution

  1. Go to your www root, check the laravel file config/session.php
  2. Check session Session Cookie Domain The default configuration is 'domain' => null, I made a change to 'domain' => 'mysite.com'.
  3. After 'php artisan cache:clear' and 'composer dump-autoload', I can login with no issue from both www.mysite.com and mysite.com

Be sure to delete your cookies from browser when testing it after these modifications are done. Old cookies can still produce problems.


tl;dr

If you need to read a given parameter state returned by a thirdparty service, you can set Socialite to avoid this checking with the stateless method:

   Socialite::driver($provider)->stateless();

I think Socialite is already prepared to avoid this issue.

https://github.com/laravel/socialite/blob/2.0/src/Two/AbstractProvider.php#L77

 /**
 * Indicates if the session state should be utilized.
 *
 * @var bool
 */
protected $stateless = false;

https://github.com/laravel/socialite/blob/2.0/src/Two/AbstractProvider.php#L374

/**
 * Indicates that the provider should operate as stateless.
 *
 * @return $this
 */
public function stateless()
{
    $this->stateless = true;
    return $this;
}

https://github.com/laravel/socialite/blob/2.0/src/Two/AbstractProvider.php#L222

/**
 * Determine if the current request / session has a mismatching "state".
 *
 * @return bool
 */
protected function hasInvalidState()
{
    if ($this->isStateless()) {
        return false; // <--------
    }
    $state = $this->request->getSession()->pull('state');
    return ! (strlen($state) > 0 && $this->request->input('state') === $state);
}

For instance, state is very useful to pass data throught google:

Parameter: state (Any string)
Provides any state that might be useful to your application upon receipt of the response. The Google Authorization Server round-trips this parameter, so your application receives the same value it sent. Possible uses include redirecting the user to the correct resource in your site, and cross-site-request-forgery mitigations.

ref: https://developers.google.com/identity/protocols/OAuth2UserAgent#overview


There are 2 major "gotchas" that none of the existing answers address.

  1. Sometimes InvalidStateException is a red herring and the true root cause is some other bug. It took me ~12 hours one time to realize that I hadn't added a new field to the $fillable array in the model.
  2. Unless you disable session state verification (as other answers here seem to want you to do but not everyone will want to do), $provider->user() can only be called once per request because the inside of that function calls hasInvalidState(), which then removes the 'state' entry from the session. It took me hours to realize that I happened to be calling $provider->user() multiple times, when I should have called it just once and saved the result to a variable.