Providers: Twitter Stateless Session store not set on request.

I’m trying to implement stateless redirecting to twitter from my Lumen app, but every time it gives an error saying: Session store not set on request.

I followed all the installation instructions of the Twitter provider correctly.

Although the provider should automatically detect I want stateless, I extra noted it in the call: return Socialite::driver('twitter')->stateless()->redirect();

Do you guys have an idea what might be wrong?

About this issue

  • Original URL
  • State: closed
  • Created 7 years ago
  • Comments: 29 (4 by maintainers)

Most upvoted comments

No one send a PR in months so the people that encounter this issue don’t seem to have that big of an issue with it. Feel free to send a PR unless it is to hacky as Twitter doesn’t support it by default.

⁣Sent from TypeApp ​

On 15 Jul 2017, 23:31, at 23:31, Bart Nagel notifications@github.com wrote:

@faustbrian, care to comment on why you closed this?

– You are receiving this because you were mentioned. Reply to this email directly or view it on GitHub: https://github.com/SocialiteProviders/Providers/issues/43#issuecomment-315560598

Hey, @tremby great idea. I didn’t want to modify directly the lib so I tried to modify my Controller, with success !

Here’s a simplified sample :

BEFORE instanciate the Socialite Driver, add a cache key to the callback uri

$cacheKey = str_random(40);
config()->set(
	'services.twitter.redirect',
	config()->get('services.twitter.redirect') . '?key=' . $cacheKey 
);
$driver = Socialite::driver('twitter');
...

( in my code the cache key is set into a class property to use it after, but here I simplified a lot)

Then when redirecting, before that, take the session value and push it into the cache :

$redirect = Socialite::driver('twitter')->redirect();
Cache::set($cacheKey, session()->pull('oauth.temp'), 1);
return $redirect;

Finally, in the callback function, before obtaining the user, take the “key” in the request, pull the cache and store it in session :

if (Request::has('key') && Cache::has(Request::get('key'))) {
	session(['oauth.temp' => Cache::pull(Request::get('key'))]);
}
$user = Socialite::driver('twitter')->user();
...
`

One potential solution which would involve cache but not session:

  1. Generate a random temporary identifier for the current user when the log in request comes in.
  2. Include this identifier in the oauth_callback parameter to the /oauth/request_token call to Twitter, such as by adding ?user=$tempId to the configured callback URL.
  3. Read the response, which includes oauth_token and oauth_token_secret, and cache these, keyed by the identifier, for some short amount of time (perhaps one minute).
  4. When the callback comes in, that identifier comes with it in a GET parameter. Retrieve the cached values, remove them from the cache, and continue logging in.

This isn’t technically stateless of course, but it avoids use of sessions and therefore cookies.

I have some proof of concept code working which I hacked into Laravel Socialite. In vendor/laravel/socialite/src/One/TwitterProvider.php I have added:

// TODO: add stateless-mode-enabling code

/**
 * Get a cache key for temporary credentials.
 *
 * @param string $tempId
 * @return string
 */
protected function getTempIdCacheKey($tempId)
{
    return 'twitter-sign-in-temp:' . $tempId;
}

/**
 * {@inheritdoc}
 */
public function redirect()
{
    // TODO: if not stateless just do parent

    // Generate a temporary identifier for this user
    $tempId = str_random(40);

    // Add encrypted credentials to configured callback URL
    $callback = $this->server->getClientCredentials()->getCallbackUri();
    $this->server->getClientCredentials()->setCallbackUri(
        $callback . (strpos($callback, '?') !== false ? '&' : '?') . http_build_query([
            'tempId' => $tempId,
        ])
    );

    // Get the temporary credentials
    $temp = $this->server->getTemporaryCredentials();

    // Cache the credentials against the temporary identifier
    app('cache')->put($this->getTempIdCacheKey($tempId), $temp, 1);

    // Redirect the user
    return new RedirectResponse($this->server->getAuthorizationUrl($temp));
}

/**
 * {@inheritdoc}
 */
protected function getToken()
{
    // TODO: if not stateless just do parent

    // Retrieve and clear the cached credentials; complain if there are none
    $cacheKey = $this->getTempIdCacheKey($this->request->input('tempId'));
    $temp = app('cache')->get($cacheKey);
    if (!$temp) {
        throw new RuntimeException('No cached credentials');
    }
    app('cache')->forget($cacheKey);

    // Get the token
    return $this->server->getTokenCredentials(
        $temp, $this->request->get('oauth_token'), $this->request->get('oauth_verifier')
    );
}

Another way to do this which would shift the temporary sliver of state to the client side might be to use a short-lived encrypted cookie.

Any thoughts?

+1 Running into the same issue

From the link you gave:

Allows a registered application to obtain an OAuth 2 Bearer Token, which can be used to make API requests on an application’s own behalf, without a user context. This is called Application-only authentication.

I could be mistaken, but I believe that is not useful for Socialite.

Well I was waiting for feedback on what I posted above.