Unable to authenticate Rails 4 Ajax CSRF token even if header is set

I'm really having problems with this, and in this case, I don't want to bypass the verify_authenticity_token filter and not change to protect_from_forgery with: :null_session .

In my request method, I set the header with the csrf token as follows:

 var token = document.querySelector("meta[name='csrf-token']").content; xhr.setRequestHeader("X-CSRF-Token", token); 

And adding a breakpoint to your controller as follows:

 def verify_authenticity_token binding.pry super end 

I checked that the header is set:

 [1] pry(#<MyController>)> request.headers => #<ActionDispatch::Http::Headers:0x007fb227cbf490 @env= {"CONTENT_LENGTH"=>"202", . . . # omitted headers . . . "HTTP_X_CSRF_TOKEN"=>"the-correct-token-from-meta-tag", . . . } 

I also tried passing the token as a parameter with the authenticity_token key (as is done with Rails forms) and setting the X-CSRF-Param tag to match (from meta[name="csrf-param"] ).

But I still get:

 Can't verify CSRF token authenticity Completed 422 Unprocessable Entity in 14638ms ActionController::InvalidAuthenticityToken (ActionController::InvalidAuthenticityToken 

Has anyone seen this before? Any thoughts on what might cause this?

Thanks in advance!

EDIT:

Following the discussion in the comments of the marflar response, it looks like the token expired when the request is made (verified compared to form_authenticity_token ). This bothers me because the token set to <%= csrf_meta_tags %> expired when the next request arrives. Any thoughts?

EDIT2: Following marflar's tips below, I added the following after_filter to my application controller:

 def set_csrf_headers response.headers['X-CSRF-Param'] = request_forgery_protection_token.to_s response.headers['X-CSRF-Token'] = form_authenticity_token end 

And I updated xhr.onload in my request method as follows:

 namespace.request = = function (type, url, opts, callback) { // code omitted xhr.onload = function () { setCSRFHeaders(xhr); var res = {data: JSON.parse(xhr.response), status: xhr.status}; return callback.call(xhr, null, res); }; // code omitted } function setCSRFHeaders ( xhr ) { var csrf_param = xhr.getResponseHeader('X-CSRF-Param'); var csrf_token = xhr.getResponseHeader('X-CSRF-Token'); if (csrf_param) { document.querySelector("meta[name='csrf-param']").content = csrf_param; } if (csrf_token) { document.querySelector("meta[name='csrf-token']").content = csrf_token; } } 

I confirmed that the response headers and then the meta tags get reset properly, however, by the time the next request arrives, this new token has expired. Thoughts?

+7
javascript ajax ruby-on-rails ruby-on-rails-4 csrf
source share
2 answers

I assume that Rails can expect the token to be in HTML, and not in the header. Can you try this? Hope this helps.

Actually, I think you can use the deprecated CRSF token because you are copying it from your template.

Usually I set it this way in my controller action:

 response.headers['X-CSRF-Param'] = "#{request_forgery_protection_token}" response.headers['X-CSRF-Token'] = "#{form_authenticity_token}" 

Does the token on your page form_authenticity_token those returned by calling form_authenticity_token ?

UPDATE

In response to your comment (below):

I just checked and you are right that this is an outdated token, which, unfortunately, leaves me even more confused. Meta tags with CSRF data are set on the page load, and at this time they correspond to form_authenticity_token, but the token is out of date until the first ajax request is made. Therefore, it doesn’t matter if I put them in HTML or as headers, as this will happen at the same time and thus will face the same problem as the token expiring before the next request is made. Thanks for your help so far - any ideas here?

I encountered such a problem when implementing login in AJAX. I found that after logging in I was unable to execute POST requests, and I needed the following code to update my token:

 var update_csrf_token_and_param_after_ajax_login = function() { $(document).on("ajaxComplete", function(event, xhr, settings) { var csrf_param = xhr.getResponseHeader('X-CSRF-Param'); var csrf_token = xhr.getResponseHeader('X-CSRF-Token'); if (csrf_param) { $('meta[name="csrf-param"]').attr('content', csrf_param); } if (csrf_token) { $('meta[name="csrf-token"]').attr('content', csrf_token); } }); } 

I think you just need to write a new token on your page before doing POST ...

+1
source share

I have the same problem. I checked the Rails source code and did the following:

  • authenticity_token did not expire on its own, so there is no need to update it after every ajax request to the server
  • no need to send either params[:authenticity_token] or header['x-csrf-token'] , only one of them, rails will check params first than header
  • on the refresh page, authenticity_token will be different, but it does not matter, since it is generated each time by one temporary pad, and the real csrf token (on the server) is time-independent.
  • real csrf token stored in session[:_csrf_token]

As you can see, the token is stored in the session, and my problem is that my session expired after 24 hours (maybe the user remains on the page for a day without updating)

If the user logged in with a cookie or some other token parameters, in any case he receives a new session and a new CSRF token is created with him, and any request with the old authenticity_token will be invalid.

So, the main problem is with the session, expired or reset.

+1
source share

All Articles