The solution to this problem can be divided into 2 phases. Phase 1 addresses the issue of ActionController::InvalidAuthenticityToken
error and phase 2 deals with the issue of long tabs waiting idly.
Phase 1(1st variation)
One way to go about is redirect the user back to their location before the error. For ex. if Alice has 3 tabs open, the first one expires and Alice logs in again in it because she was browsing on it. But when she moves to tab 3 which has URL 'http://example.com/ex' and submits a form. Now instead of displaying her an error we can redirect her back to 'http://example.com/ex' with her submitted form values already pre-filled in the form for easy use.
This can be achieved by following this approach:
1) ApplicationController - Add this function:
def handle_unverified_request
flash[:error] = 'Kindly retry.' # show this error in your layout
referrer_url = URI.parse(request.referrer) rescue URI.parse(some_default_url)
# need to have a default in case referrer is not given
# append the query string to the referrer url
referrer_url.query = Rack::Utils.parse_nested_query('').
merge(params[params.keys[2]]). # this may be different for you
to_query
# redirect to the referrer url with the modified query string
redirect_to referrer_url.to_s
end
2) You need to include a default value for all your form fields. It will be the name of that field.
...
<% f.text_field, name: 'email', placeholder: 'Email', value: params[:email] %>
...
This way whenever Alice will submit a form with wrong authenticity_token
she will be redirected back to her form with the original values she submitted and she will be shown a flash message that kindly retry your request.
Phase 1(2nd variation)
Another way to go about is just redirect Alice back to the form which she submitted without any pre-filled values.
This approach can be achieved by:
1) ApplicationController - Add this function:
def handle_unverified_request
flash[:error] = 'Kindly retry.'
redirect_to :back
end
Phase 2
To tackle the problem of long awaited tabs you can take the help of SSEs. Rails 4 has ActionController::Live
for handling SSEs.
1) Add this to any controller:
include ActionController::Live
...
def sse
response.headers['Content-Type'] = 'text/event-stream'
sse = SSE.new(response.stream, retry: 2000, event: 'refresh') # change the time interval to your suiting
if user_signed_in? # check if user is signed-in or not
sse.write('none')
else
sse.write('refresh')
end
ensure
sse.close
end
2) Give the above function a GET
route in your routes file. Lets call this route '/sse'
3) Add this in your layout:
<% if user_signed_in? %> # check if user is signed-in or not
<script>
var evtSource = new EventSource("/sse");
evtSource.addEventListener('refresh', function(e){
if(e.data == 'refresh'){
window.location = window.location.href;
}
});
</script>
<% end %>
Note: using EventSource is not supported by all browsers. Please check out the Browser compatibility section.
Source:
rails 4 redirect back with new params & MDN: Using server-sent events