This answer is in reference to version 5.4, perhaps previous versions but I haven't tested those.
The root of the problem is the CSRF token is expired on the client side which makes any POST to the server fail with that token.
IF you're using AJAX, you could use the API routes which do not do CSRF verification by default.
You could turn off CSRF verification for specific URIs. In this case, I'm turning off CSRF verification for /logout
. This approach works well if you truly want to exclude certain URIs from verification.
app/Http/Middleware/VerifyCsrfToken.php
/**
* The URIs that should be excluded from CSRF verification.
*
* @var array
*/
protected $except = [
'/logout'
];
In addition, you should handle the CSRF error when it occurs in a way that works well for your application. Below is an example of something very basic you could do.
app/Exceptions/Handler.php
/**
* Render an exception into an HTTP response.
*
* @param \Illuminate\Http\Request $request
* @param \Exception $exception
* @return \Illuminate\Http\Response
*/
public function render($request, Exception $exception)
{
if($exception instanceof \Illuminate\Session\TokenMismatchException){
// token mismatch is a security concern, ensure logout.
Auth::logout();
// Tell the user what happened.
session()->flash('alert-warning','Your session expired. Please login to continue.');
// Go to login.
return redirect()->route('login');
}
return parent::render($request, $exception);
}
By the way, to test both of these changes, you can modify the session settings. I just set the lifetime to 1
for testing. Then, set it back when you're finished (it is 120 by default). You'll want to login, load your form page, wait over a minute, then attempt your POST.
config/session.php
/*
|--------------------------------------------------------------------------
| Session Lifetime
|--------------------------------------------------------------------------
|
| Here you may specify the number of minutes that you wish the session
| to be allowed to remain idle before it expires. If you want them
| to immediately expire on the browser closing, set that option.
|
*/
'lifetime' => 1,