Graceful InvalidAuthenticityToken exception handling in Rails 4

I just upgraded an application from Rails 3 to Rails 4 and I see a bunch of InvalidAuthenticityToken exceptions. Digging in it looks like it is quite common for our users to open several long-lived tabs on our site. So I think something similar happens: user Alice opens three tabs, and her session expires. She registers on one of the tabs, which updates the authentication token stored in her session. Then she returns to one of the other open tabs and tries to send data, but receives a 500 error from the InvalidAuthenticityToken error that we raised.

It would be nice to do some error handling for Alice so that she doesn't get the 500 error. I'm curious about the best practices for this kind of situation. What would be a good way to handle Alice's view on an expired tab? I do not want to expire the current session, because it would be very unpleasant from the point of view of the user ("I just logged in, you take a long time!"). Ideally, I just want the user to reload the page, which will cause the correct authentication token to be set on the form. Or should I do something else so that the open long-awaited tabs open a notification that the session has expired and force restart? This probably would not be optimal from the user's point of view, because they liked the page to be ready and easily accessible for storing links, so they left it open on the tab in the first place.

+5
source share
2 answers

The solution to this problem can be divided into two phases. Phase 1 fixes the ActionController::InvalidAuthenticityToken error ActionController::InvalidAuthenticityToken , and phase 2 addresses the issue of long tabs without waiting.

Phase 1 (1st variation)

One way is to redirect the user back to their location before the error. E.g. if Alice has 3 tabs open, the first expires, and Alice enters her again because she is viewing it. But when it moves to tab 3, which has the URL ' http://example.com/ex ' and submits the form. Now, instead of displaying its error, we can redirect it back to http://example.com/ex 'with its submitted form values โ€‹โ€‹that have already been pre-filled in the form for ease of 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 specify a default value for all form fields. This will be the name of this field.

 ... <% f.text_field, name: 'email', placeholder: 'Email', value: params[:email] %> ... 

Thus, whenever Alice submits a form with the wrong authenticity_token , she will be redirected back to her form with the original values โ€‹โ€‹that she submitted, and she will be shown a flash message that will voluntarily repeat your request.

Phase 1 (second variation)

Another way is to simply redirect Alice back to the form she submitted without any pre-populated 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 solve the problem of long-awaited tabs, you can use SSE. Rails 4 has an ActionController::Live to handle SSE.

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) Provide the above function a GET route in your routes file. Lets call this route '/ sse'

3) Add this to 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. Check your browser compatibility section .

Source: rails 4 redirect back with new parameters and MDN: use events sent by server

+9
source

In my application 4.1.1 4.1.1 I had the same problem. I solved this by adding this code to ApplicationController. I found this solution here .

 rescue_from ActionController::InvalidAuthenticityToken, with: :redirect_to_referer_or_path def redirect_to_referer_or_path flash[:notice] = "Please try again." redirect_to request.referer end 

This way, any controller that inherits the ApplicationController will handle the error, redirecting to the page from which the form was submitted, using a flash message to give the user some idea of โ€‹โ€‹what went wrong. Note that the hash syntax introduced in Ruby 1.9 is used. For older versions of Ruby, you will need to use :with => :redirect_to_referer_or_path

+1
source

Source: https://habr.com/ru/post/1246743/


All Articles