I'm really having trouble with this, and in this instance, I neither want to skip the verify_authenticity_token
filter, nor change to protect_from_forgery with: :null_session
.
In my request method, I am setting a header with the csrf token as follows:
var token = document.querySelector("meta[name='csrf-token']").content;
xhr.setRequestHeader("X-CSRF-Token", token);
And by inserting a breakpoint in my controller like so:
def verify_authenticity_token
binding.pry
super
end
I have verified 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 have also tried passing the token as a param with the key authenticity_token
(as is done with Rails forms), and set the X-CSRF-Param
tag to match (from meta[name="csrf-param"]
).
Yet I am still getting:
Can't verify CSRF token authenticity
Completed 422 Unprocessable Entity in 14638ms
ActionController::InvalidAuthenticityToken (ActionController::InvalidAuthenticityToken
Anyone seen this before? Any thoughts on what might cause this?
Thanks in advance!
EDIT:
Following discussion in the comments of marflar's answer, it looks like the token has expired when the request is made (tested by comparing to form_authenticity_token
). This is confusing me further, as the token set in <%= csrf_meta_tags %>
is expired when the next request comes in. Any thoughts?
EDIT2: Following marflar's advice below, I added the following after_filter
to my app 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 verified that the response headers, and then the meta tags are getting reset properly, however, by the time the next request comes in, this new token is expired again. Thoughts?