I know this has been brought up a few times on SO but I cannot make inroads. I'm on Rails 5.0.7.2.
I am getting ActionController::InvalidCrossOriginRequest
thrown hundreds of times per day. Rollbar says it is mostly caused by Bingbot, and it's on my user signup modal, which is from Devise. It's a GET request, and the path is /users/sign_up
.
NOTE: It's not a form. It's a modal that simply asks the user How would you like to sign up? Email, Fb, Google auth, etc.
Basically I have some javascript actions on my site, picture like a reddit or stackoverflow upvote. I am not using remote: true
on those links, but rather I have an $.ajax
call that handles the upvote like so:
var token = document.querySelector('meta[name="csrf-token"]').content;
$.ajax({
// For the upvote, type will be POST
type: $this.data('method'),
beforeSend: function(xhr) {
xhr.setRequestHeader('X-CSRF-Token', token)
},
url: $this.data('url'),
success: function(data) {
// Some cleanup...
}
});
Once a logged out visitor tries to upvote, it fires the ajax request which is then halted (not authorized) and redirected, like this:
Started POST "/upvotes/toggle_vote"
Processing by UpvotesController#toggle_vote as */*
Completed 401 Unauthorized in 1ms (ActiveRecord: 0.0ms)
Started GET "/users/sign_up"
Processing by RegistrationsController#new as */*
Here's the Devise controller which I've overwritten:
class RegistrationsController < Devise::RegistrationsController
def new
# ... other code
respond_to do |format|
format.js { render layout: false }
format.html { respond_with self.resource }
end
end
end
Inside of /views/devise/registrations
I have new.js.erb
which opens up the "how would you like to sign up?" modal.
WHAT I'VE TRIED:
I don't think solving it by adding
protect_from_forgery except: :new
is the right answer (as has been proposed in similar questions), since that's inherently not safe.I tried reordering the
respond_to
formats per this but that makes the signup modal not open.I tried to throw in a
format.any { raise ActionController::RoutingError.new("Not Found") }
catch-all in therespond_to
block but that did nothing to fix the error.As you can see in the
$.ajax
call above I even try to set theX-CSRF-Token
header.
The full error text by the way is this:
ActionController::InvalidCrossOriginRequest: Security warning: an embedded <script> tag on another site requested protected JavaScript. If you know what you're doing, go ahead and disable forgery protection on this action to permit cross-origin JavaScript
Any ideas?
Could it be that the authenticity token is getting lost since UpvotesController#toggle_vote
redirects to RegistrationsController#new
and changes from a POST to a GET?
I know this has been brought up a few times on SO but I cannot make inroads. I'm on Rails 5.0.7.2.
I am getting ActionController::InvalidCrossOriginRequest
thrown hundreds of times per day. Rollbar says it is mostly caused by Bingbot, and it's on my user signup modal, which is from Devise. It's a GET request, and the path is /users/sign_up
.
NOTE: It's not a form. It's a modal that simply asks the user How would you like to sign up? Email, Fb, Google auth, etc.
Basically I have some javascript actions on my site, picture like a reddit or stackoverflow upvote. I am not using remote: true
on those links, but rather I have an $.ajax
call that handles the upvote like so:
var token = document.querySelector('meta[name="csrf-token"]').content;
$.ajax({
// For the upvote, type will be POST
type: $this.data('method'),
beforeSend: function(xhr) {
xhr.setRequestHeader('X-CSRF-Token', token)
},
url: $this.data('url'),
success: function(data) {
// Some cleanup...
}
});
Once a logged out visitor tries to upvote, it fires the ajax request which is then halted (not authorized) and redirected, like this:
Started POST "/upvotes/toggle_vote"
Processing by UpvotesController#toggle_vote as */*
Completed 401 Unauthorized in 1ms (ActiveRecord: 0.0ms)
Started GET "/users/sign_up"
Processing by RegistrationsController#new as */*
Here's the Devise controller which I've overwritten:
class RegistrationsController < Devise::RegistrationsController
def new
# ... other code
respond_to do |format|
format.js { render layout: false }
format.html { respond_with self.resource }
end
end
end
Inside of /views/devise/registrations
I have new.js.erb
which opens up the "how would you like to sign up?" modal.
WHAT I'VE TRIED:
I don't think solving it by adding
protect_from_forgery except: :new
is the right answer (as has been proposed in similar questions), since that's inherently not safe.I tried reordering the
respond_to
formats per this but that makes the signup modal not open.I tried to throw in a
format.any { raise ActionController::RoutingError.new("Not Found") }
catch-all in therespond_to
block but that did nothing to fix the error.As you can see in the
$.ajax
call above I even try to set theX-CSRF-Token
header.
The full error text by the way is this:
ActionController::InvalidCrossOriginRequest: Security warning: an embedded <script> tag on another site requested protected JavaScript. If you know what you're doing, go ahead and disable forgery protection on this action to permit cross-origin JavaScript
Any ideas?
Could it be that the authenticity token is getting lost since UpvotesController#toggle_vote
redirects to RegistrationsController#new
and changes from a POST to a GET?
- Does this answer your question? Rails InvalidCrossOriginRequest – Juan Artau Commented Apr 17, 2021 at 0:08
-
It does not unfortunately, because I think
protect_from_forgery
should be there for security. There was no other proposed solution in that thread. Also, I am not mixing http/https resources. – DelPiero Commented Apr 19, 2021 at 14:20 -
how about:
protect_from_forgery unless: -> { request.format.js? }
, only for your some javascript actions. – Lam Phan Commented Apr 20, 2021 at 8:21 - Can you share your code for the javascript action (front end)? And also - are you using jquery-rails or rails-ujs? – Joel Blum Commented Apr 21, 2021 at 17:21
-
@Joel_Blum I'm using jquery-rails. I cleaned up the question a bit, you can now see my
$.ajax
call. – DelPiero Commented May 17, 2021 at 16:45
5 Answers
Reset to default 3 +200Couple of thoughts:
You shouldn't put the csrf token yourself, both jquery-rails or rails-ujs (I'm not sure which one you're using) do it for you; with rails-ujs you need to use the
Rails.ajax
methodThere is no redirecting POST to GET and keeping the original POST headers , that means you indeed lose the csrf token in GET request (however you usually don't send or need a CSRF token in a GET request anyway so check why you are doing that maybe).
You need to send an authenticity token with ajax requests that modify resources in the server (post/put/patch/delete). Usually, rails add it for you as a hidden field in the form.
Adding this inside your form should do the trick:
<% hidden_field_tag :authenticity_token, form_authenticity_token %>
It looks like a CORS issue to me, just like the error suggest there I an API call made fro an embedded javascript which is on a different domain than the one defined on your server. This triggers a CORS error. Usually to fix CORS error you need to add a header Access-Control-Allow-Origin with the the domain of the website in the list of allowed domain. You can easily do this with the rack cors gem: https://github./cyu/rack-cors
In Rails 5 I found that
config.action_view.embed_authenticity_token_in_remote_forms = true
avoid the CORS issue on same domain remote calls, but it seems that in Rail 6 is not effective.