I think the answer by @UX Labs is misleading.
And then the comment from @jfadich seems completely incorrect.
For Laravel 5.4 in May 2017, I solved the problem this way:
Here Is an Answer That Works
In web.php
:
Route::post('keep-token-alive', function() {
return 'Token must have been valid, and the session expiration has been extended.'; //https://stackoverflow.com/q/31449434/470749
});
In javascript in your view:
$(document).ready(function () {
setInterval(keepTokenAlive, 1000 * 60 * 15); // every 15 mins
function keepTokenAlive() {
$.ajax({
url: '/keep-token-alive', //https://stackoverflow.com/q/31449434/470749
method: 'post',
headers: {
'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
}
}).then(function (result) {
console.log(new Date() + ' ' + result + ' ' + $('meta[name="csrf-token"]').attr('content'));
});
}
});
Note that you must not list 'keep-token-alive'
in the exclusions within VerifyCsrfToken.php
. As @ITDesigns.eu implied in a comment, it's important for this route to verify that there is a valid token currently and that it just needs to have its expiration extended.
Why this approach solves my problem
My Laravel site allows users to watch a video (an hour long), and it uses ajax to post their progress every minute.
But many users load the page and then don't start the video until many hours later.
I don't know why they leave their browser tab open so long before watching, but they do.
And then I'd get a ton of TokenMismatch exceptions in my logs (and would miss out on the data of their progress).
In session.php
, I changed 'lifetime'
from 120 to 360 minutes, but that still wasn't enough. And I didn't want to make it longer than 6 hours. So I needed to enable this one page to frequently extend the session via ajax.
How you can test it and get a sense for how the tokens work:
In web.php
:
Route::post('refresh-csrf', function() {//Note: as I mentioned in my answer, I think this approach from @UX Labs does not make sense, but I first wanted to design a test view that used buttons to ping different URLs to understand how tokens work. The "return csrf_token();" does not even seem to get used.
return csrf_token();
});
Route::post('test-csrf', function() {
return 'Token must have been valid.';
});
In javascript in your view:
<button id="tryPost">Try posting to db</button>
<button id="getNewToken">Get new token</button>
(function () {
var $ = require("jquery");
$(document).ready(function () {
$('body').prepend('<div>' + new Date() + ' Current token is: ' + $('meta[name="csrf-token"]').attr('content') + '</div>');
$('#getNewToken').click(function () {
$.ajax({
url: '/refresh-csrf',
method: 'post',
headers: {
'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
}
}).then(function (d) {
$('meta[name="csrf-token"]').attr('content', d);
$('body').prepend('<div>' + new Date() + ' Refreshed token is: ' + $('meta[name="csrf-token"]').attr('content') + '</div>');
});
});
$('#tryPost').click(function () {
$.ajax({
url: '/test-csrf',
method: 'post',
headers: {
'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
}
}).then(function (d) {
$('body').prepend('<div>' + new Date() + ' Result of test: ' + d + '</div>');
});
});
});
})();
In session.php
, temporarily change 'lifetime'
to something very short for testing purposes.
Then play around.
This is how I learned how the Laravel token works and how we really just need to successfully POST to a CSRF-protected route frequently so that the token continues to be valid.