Rails - InvalidAuthenticityToken for json / xml requests

For some reason, I get an InvalidAuthenticityToken when sending mail requests to my application when using json or xml. I understand that rails should require an authentication token only for html or js requests, and therefore I should not encounter this error. The only solution I have found so far is to disable_from_forgery protection for any actions that I would like to get through the API, but this is not ideal for obvious reasons. Thoughts?

def create respond_to do |format| format.html format.json{ render :json => Object.create(:user => @current_user, :foo => params[:foo], :bar => params[:bar]) } format.xml{ render :xml => Object.create(:user => @current_user, :foo => params[:foo], :bar => params[:bar]) } end end 

and this is what I get in the logs whenever I submit an action request:

  Processing FooController#create to json (for 127.0.0.1 at 2009-08-07 11:52:33) [POST] Parameters: {"foo"=>"1", "api_key"=>"44a895ca30e95a3206f961fcd56011d364dff78e", "bar"=>"202"} ActionController::InvalidAuthenticityToken (ActionController::InvalidAuthenticityToken): thin (1.2.2) lib/thin/connection.rb:76:in `pre_process' thin (1.2.2) lib/thin/connection.rb:74:in `catch' thin (1.2.2) lib/thin/connection.rb:74:in `pre_process' thin (1.2.2) lib/thin/connection.rb:57:in `process' thin (1.2.2) lib/thin/connection.rb:42:in `receive_data' eventmachine (0.12.8) lib/eventmachine.rb:242:in `run_machine' eventmachine (0.12.8) lib/eventmachine.rb:242:in `run' thin (1.2.2) lib/thin/backends/base.rb:57:in `start' thin (1.2.2) lib/thin/server.rb:156:in `start' thin (1.2.2) lib/thin/controllers/controller.rb:80:in `start' thin (1.2.2) lib/thin/runner.rb:174:in `send' thin (1.2.2) lib/thin/runner.rb:174:in `run_command' thin (1.2.2) lib/thin/runner.rb:140:in `run!' thin (1.2.2) bin/thin:6 /opt/local/bin/thin:19:in `load' /opt/local/bin/thin:19 
+14
json ruby-on-rails restful-authentication
Aug 07 '09 at 16:00
source share
6 answers

When enabled, protect_from_forgery Rails requires an authentication token for any non-GET request. Rails will automatically add an authentication token to forms created using form helpers or links created using AJAX helpers, so in normal cases you won’t have to think about it.

If you do not use the built-in Rails form or AJAX helpers (you may be running unobtrusive JS or using the JS MVC framework), you will need to set the token yourself on the client side and send it along with your data when sending a POST request. You would put such a line in the <head> your layout:

 <%= javascript_tag "window._token = '#{form_authenticity_token}'" %> 

Then your AJAX function will send a token with your other data (jQuery example):

 $.post(url, { id: theId, authenticity_token: window._token }); 
+19
Oct 26 '09 at 22:59
source share

I had a similar situation, and the problem was that I did not send the correct content content through the headers - I requested text/json , and I had to request application/json .

I used curl following to test my application (modify if necessary):

 curl -H "Content-Type: application/json" -d '{"person": {"last_name": "Lambie","first_name": "Matthew"}}' -X POST http://localhost:3000/people.json -i 

Or you can save JSON to a local file and call curl like this:

 curl -H "Content-Type: application/json" -v -d @person.json -X POST http://localhost:3000/people.json -i 

When I changed the content type headers to the right of application/json , all my problems were gone and I no longer needed to disable fake protection.

+11
Oct 22 '09 at 16:54
source share

This is the same as @ user1756254 answer , but in Rails 5 you need to use a slightly different syntax:

 protect_from_forgery unless: -> { request.format.json? } 

Source: http://api.rubyonrails.org/v5.0/classes/ActionController/RequestForgeryProtection.html

+9
Sep 19 '16 at 21:27
source share

Another way is to exclude authenticity_token checking with skip_before_filter in your Rails application:

 skip_before_action :verify_authenticity_token, only: [:action1, :action2] 

This will allow curls to do their job.

+4
Feb 05 '14 at 21:15
source share

By adding answers to andymism, you can use this to apply the default TOKEN inclusion in every POST request:

 $(document).ajaxSend(function(event, request, settings) { if ( settings.type == 'POST' || settings.type == 'post') { settings.data = (settings.data ? settings.data + "&" : "") + "authenticity_token=" + encodeURIComponent( window._token ); } }); 
+2
Oct 28 '09 at 0:38
source share

To add an answer to Fernando, if your controller answers both json and html, you can use:

  skip_before_filter :verify_authenticity_token, if: :json_request? 
+2
Dec 19 '14 at 19:46
source share



All Articles