Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Still getting TokenMismatchException after Laravel session lifetime #22

Closed
ivanvermeyen opened this issue Feb 19, 2016 · 15 comments
Closed

Comments

@ivanvermeyen
Copy link

Hi

When I leave a form open for a while, I still get a TokenMismatchException.
I'm using Laravel 5.2.

For testing I've set the session lifetime in config/session.php to 3 minutes and the drip interval to 1 min (60000ms). The drip route is being called every minute as it should (no errors in the Chrome network tab).

After the first drip I can still send the form, but after 3 drips or minutes I get the Token exception.

TokenMismatchException in VerifyCsrfToken.php line 67:

Seems the session isn't actually being refreshed?

@ivanvermeyen
Copy link
Author

Update:

Did some testing...
If I open any page in a second tab and refresh it before the session expires, the form keeps working.

First I was thinking that maybe ajax was caching the request, so I tried to ping your route with jQuery and with cache disabled, but the session still expired. So that's not the issue.

    setInterval(function () {
        $.ajax({
            url: '/genealabs/laravel-caffeine/drip',
            success: function () {
                console.log('pinged');
            },
            cache: false
        });
    }, 60000);

If I ping a normal route (that returns my home view), the form keeps working and the session doesn't expire.

    setInterval(function () {
        $.ajax({
            url: '/',
            success: function () {
                console.log('pinged');
            },
            cache: false
        });
    }, 60000);

Finally I got it to work by commenting out your package route and creating my own route.

If I get rid of the default controller namespace App\Http\Controllers, I can directly point to your controller:

use GeneaLabs\LaravelCaffeine\Http\Controllers\LaravelCaffeineController;
Route::get('genealabs/laravel-caffeine/drip', ['uses' => LaravelCaffeineController::class.'@drip']);

If I don't bypass the namespace I need to create my own route and controller method:

Route::get('genealabs/laravel-caffeine/drip', ['uses' => 'PagesController@drip']);

and

public function drip()
{
    return response('', 204);
}

So why your package route is not keeping the session alive is still a mystery to me...

@ivanvermeyen
Copy link
Author

Update 2: Found the issue! :)

In your routes.php file Route::getMiddleware() returns only the protected $routeMiddleware in Kernel.php. So Route::getMiddleware()['web'] is never set. Because the drip route is then registered outside of a 'web' route group, it has no knowledge of sessions etc.

Haven't found a proper way to check if a route group is set though...

@mikebronner
Copy link
Owner

Thanks for reporting and looking into this. I'm on the road this weekend but will look into it as well if I can.

@mikebronner
Copy link
Owner

@ivanvermeyen thanks again for your research on this. Can you check if release 0.3.4 fixes this? Go ahead an re-open if the issue persists. :)

@ivanvermeyen
Copy link
Author

Got an error:

ErrorException in routes.php line 18:
in_array() expects parameter 2 to be array, string given
...
at in_array('web', 'web') in routes.php line 18
at hasWebMiddleware() in routes.php line 5
at require('/home/vagrant/Code/site/vendor/genealabs/laravel-caffeine/src/Http/routes.php') in LaravelCaffeineServiceProvider.php line 12

This works for me:

    if (in_array('web', (array) $route->getAction()['middleware'])) {
        return true;
    }

Mind the cast to (array).

@mikebronner
Copy link
Owner

Thanks for the follow-up. What version of Laravel do you have installed and what version of PHP are you using? It looks like this is down to versioning differences, as this worked for me. getAction is actually returning arrays for me, so not sure what the difference is.

@mikebronner mikebronner reopened this Feb 20, 2016
@ivanvermeyen
Copy link
Author

I am running the latest Laravel (5.2.20) on a Homestead box with PHP 5.6.

@ivanvermeyen
Copy link
Author

Just a thought.
Middleware groups are only relevant since Laravel 5.2, right?

Perhaps you can do a version check instead?

function hasWebMiddleware()
{
    return version_compare(app()->version(), '5.2', '>=');
}

If using Laravel >= 5.2 and no 'web' group exists, adding the route to that group doesn't seem to throw any errors, so it can't hurt?

Or perhaps you can add middleware groups to the config file, in case people rename their groups?

Edit:

Might be better to use App::version(), I'm not sure when app() was introduced.

@mikebronner
Copy link
Owner

Yea, this is getting really tricky. I will cast to array for now.

@mikebronner
Copy link
Owner

Should now be fixed in 0.3.5

@ivanvermeyen
Copy link
Author

$actions['middleware'] returns a string 'web'for me, which causes in_array to choke.

@mikebronner
Copy link
Owner

Sorry, working while traveling on the road ... not sure why that is happening for you. Do you only have a single middleware group? I wonder if it returns as string if there's only a single group, and array if multiple. REALLY ANNOYING. Could also be a difference between PHP 5.x and PHP 7. Thanks for your patience on this.

@ivanvermeyen
Copy link
Author

There are 2 default middleware groups in my Kernel.php: 'web' and 'api'. But I only use 'web' in my routes.php. It's strange indeed.

I've been searching through the Laravel code and found this reference, but it doesn't really help much. :) It's fun to try and understand the inner workings tho. It just requires a lot of digging :)

Thanks for taking the time to look into this so fast and enjoy your weekend! :) 👍

@mikebronner
Copy link
Owner

Is it working now with the array casting? :)

@ivanvermeyen
Copy link
Author

It's working now :)
And... I just discovered why I got a string instead of an array...

https://laravel.com/docs/5.2/middleware#registering-middleware

According to the docs you can do:

Route::get('admin/profile', ['middleware' => 'auth', function () {
    //
}]);

or:

Route::get('/', ['middleware' => ['first', 'second'], function () {
    //
}]);

I was using the first one with 'web'. So it seems whatever you specify there is what is returned from the getAction()['middleware'].

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants