Proper way to send an Authenticity Token with AJAX to Rails

38,482

Solution 1

Actually, you are reading the action attribute of form and sending a post ajax request to it. to send form data you have to submit the form or you can serialize the form data and send it in ajax request like

$(".ajax-referral").click(function(){
  $.ajax({
      type: "POST", 
      url: $(this).parent("form").attr("action") + "?&authenticity_token=" + AUTH_TOKEN, 
      data:$(this).parent("form").serialize(),
      dataType: "script"
      });
  return false;
});

Doing this will serialize your form data and send it along with ajax request and authenticity token is already being sent via query string

Solution 2

This token also already appears in one of the "meta" tags in the head of the application.html.erb layout file by default if you have the following ERB at the top:

<%= csrf_meta_tag %>

That ERB roughly renders to:

<meta content="abc123blahblahauthenticitytoken" name="csrf-token">

You can then grab it using jQuery with the following code:

var AUTH_TOKEN = $('meta[name=csrf-token]').attr('content');

Solution 3

None of these worked for me until I set the X-CSRF-Token value on the request header via JS like this:

request.setRequestHeader('X-CSRF-Token', token)

token of course, being the CSRF token. I got this from the <meta name="csrf-token"> tag and did not use encodeURIComponent()

Update since this is proving useful to some

So all in all:

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

Solution 4

Thanks!

Just to clarify for the more common use.

You need the js tag with var AUTH_TOKEN in your head. Should be something like this.

<%= csrf_meta_tag %>
<%= javascript_tag "var AUTH_TOKEN = '#{form_authenticity_token}';" if protect_against_forgery? %>

And then simply put your authenticity_token=AUTH_TOKEN in the ajax data if you don't need to use parent(form) or something like this.

$.ajax({
  type: 'post',
  dataType:'text',
  data: "user_id="+user_id+"&authenticity_token="+AUTH_TOKEN,
  url:'/follow/unfollow'
})

Thanks to the guys above for sharing this knowledge!

Solution 5

In Rails 5.1+ a CSRF token is automatically appended if you use a built-in Rails JS helper for AJAX requests(from rails-ujs), example:

Rails.ajax({
  url: "/your/url",
  type: "POST",
  data: "a=1&b=2",
  success: function(data) {
    console.log(data);
  }
});

This library also provides you a helper to get CSRF token manually if you need it with:

Rails.csrfToken();
Share:
38,482
Trip
Author by

Trip

I program Ruby, C#, iOS, Node, and Augmented Reality for Unity3D. I write PostgreSQL, mySQL, SQLite, and MongoDB. I use Heroku, Amazon, Microsoft Azure. Creator of the Yoga Sutras App, Braidio Mobile, and Braidio. In my spare time, I teach Ashtanga Yoga. elephant trip AT gmail DOT com #happyToHelp

Updated on July 09, 2022

Comments

  • Trip
    Trip almost 2 years

    This works but gets stopped because it lacks an authenticity token:

    $(".ajax-referral").click(function(){
      $.ajax({type: "POST", url: $(this).parent("form").attr("action"), dataType: "script"});
      return false;
    });
    

    So I tried adding it like so:

    $(".ajax-referral").click(function(){
      $.ajax({type: "POST", url: $(this).parent("form").attr("action") + "?&authenticity_token=" + AUTH_TOKEN, dataType: "script"});
      return false;
    });
    

    And it passes the auth_token correctly as a param, but seems to lose the rest of my form.

    Anyways to accomplish both sending the form data that works, and the authenticity token as well?

    This is a rails environment. And I have this in my head.

    = javascript_tag "var AUTH_TOKEN = '#{form_authenticity_token}';" if protect_against_forgery?
    

    Things I've tried

    1.

    = hidden_field :authenticity_token, :value => form_authenticity_token
    

    2.

    $.ajax({type: "POST", url: $(this).parent("form").attr("action"), dataType: "script", authenticity_token: AUTH_TOKEN});
    

    3.

    // Always send the authenticity_token with ajax
    $(document).ajaxSend(function(event, request, settings) {
        if ( settings.type != 'GET' ) {
            settings.data = (settings.data ? settings.data + "&" : "")
                + "authenticity_token=" + encodeURIComponent( AUTH_TOKEN );
        }
    });
    
  • Ken
    Ken over 9 years
    I had to use encodeURIComponent( AUTH_TOKEN ), but using it in the query string worked once I did this.
  • Bryan Larsen
    Bryan Larsen over 9 years
    <input type="hidden" name="authenticity_token" value={$('meta[name=csrf-token]').attr('content')}/> (Note that I'm using JSX for templating. Replace the { with whatever's appropriate for your template language.
  • iblue
    iblue almost 8 years
    The auth token is base64 encoded. It may contain a plus sign (+). If you don't properly escape the auth token, rails will parse it as a space, because of url encoding.
  • Gacci
    Gacci over 6 years
    token should not be sent in the url!
  • Fabrizio Bertoglio
    Fabrizio Bertoglio about 5 years
    may not be possible in rails 5 as each form get his own auth token blog.bigbinary.com/2016/01/11/…
  • omdel
    omdel about 5 years
    Good point, @FabrizioBertoglio I think this comment was written around Rails 3/Rails 4 days. May not be possible to do it this way in later versions.
  • tadman
    tadman almost 5 years
    This is the solution for Rails 5+ with the gotcha that per_form_csrf_tokens must be set for Single Page Application situations.
  • Julien
    Julien over 3 years
    @Gacci unless your endpoint URL is http (i.e. plain-text), the params (authenticity_token in this case) will also be encrypted. So I'd rather say "token should always be sent to httpS URLs". Ajax-called URLs are not kept in browser history anyway.