PHP Snippet 1:
<html>
<head>
<meta name="csrf_token" content="{{ csrf_token() }}">
</head>
<body>
<script type="text/javascript">
var csrfToken = $('[name="csrf_token"]').attr('content');
setInterval(refreshToken, 3600000); // 1 hour
function refreshToken(){
$.get('refresh-csrf').done(function(data){
csrfToken = data; // the new token
});
}
setInterval(refreshToken, 3600000); // 1 hour
</script>
</body>
</html>
PHP Snippet 2:
Route::get('refresh-csrf', function(){
return csrf_token();
});
PHP Snippet 3:
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
});
PHP Snippet 4:
$(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'));
});
}
});
PHP Snippet 5:
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.';
});
PHP Snippet 6:
<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>');
});
});
});
})();
PHP Snippet 7:
//In config/session.php replace this:
'lifetime' => 120
//with:
'lifetime' => 360
PHP Snippet 8:
//In app/Exceptions/Handler.php replace this:
public function render($request, Exception $e)
{
if ($e instanceof ModelNotFoundException) {
$e = new NotFoundHttpException($e->getMessage(), $e);
}
return parent::render($request, $e);
}
//with:
public function render($request, Exception $e)
{
if ($e instanceof ModelNotFoundException) {
$e = new NotFoundHttpException($e->getMessage(), $e);
}
if ($e instanceof \Illuminate\Session\TokenMismatchException) {
return redirect('/')->withErrors(['token_error' => 'Sorry, your session seems to have expired. Please try again.']);
}
return parent::render($request, $e);
}
PHP Snippet 9:
@if ($errors->has('token_error'))
{{ $errors->first('token_error') }}
@endif
PHP Snippet 10:
/*
|--------------------------------------------------------------------------
| 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' => 60 * 24 * 7, // Set session lifetime to 1 week
'expire_on_close' => true,
PHP Snippet 11:
<div>
@if(count($errors)>0)
@foreach($errors->all() as $error)
<ul>
<li>{{$error}}</li>
</ul>
@endforeach
@endif
</div>
PHP Snippet 12:
/*
|--------------------------------------------------------------------------
| 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' => 120,
PHP Snippet 13:
$router->get('csrf-token', function() {
return request()->session()->token();
});
PHP Snippet 14:
handleNewCsrfToken();
// Use visbility API to make sure the token gets updated in time, even when the device went to sleep.
document.addEventListener('visibilitychange', function() {
if (document.visibilityState === 'visible') {
setTimeoutToRefreshCsrfToken();
} else if (document.visibilityState === 'hidden') {
clearTimeout(refreshCsrfTokenTimeout);
}
});
function handleNewCsrfToken() {
updateCsrfTokenTimeoutTarget();
setTimeoutToRefreshCsrfToken();
}
function updateCsrfTokenTimeoutTarget() {
csrfTokenTimeoutTarget = moment().add(2, 'hour').subtract(5, 'minute');
}
function setTimeoutToRefreshCsrfToken() {
refreshCsrfTokenTimeout = setTimeout(refreshCsrfToken, csrfTokenTimeoutTarget.diff());
}
function refreshCsrfToken() {
axios.get('/csrf-token').then(function(response) {
document.getElementsByName('_token').forEach(function(element) {
element.value = response.data;
handleNewCsrfToken();
});
});
}
PHP Snippet 15:
@guest
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" />
<meta http-equiv="Pragma" content="no-cache" />
<meta http-equiv="Expires" content="0" />
<meta http-equiv="refresh" content="{{config('session.lifetime') * 60}}">
@endguest
PHP Snippet 16:
$(window).load(function(){
$.ajaxSetup({
statusCode: {
419: function(){
location.reload();
}
}
});
});
PHP Snippet 17:
<script type="text/javascript">
//reload on current page
window.location = '';
</script>
PHP Snippet 18:
<script type="text/javascript">
var timeout = ({{config('session.lifetime')}} * 60) * 1000;
setTimeout(function() {
//reload on current page
window.location = '';
}, timeout);
</script>