44

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 );
    }
});
tadman
  • 208,517
  • 23
  • 234
  • 262
Trip
  • 26,756
  • 46
  • 158
  • 277

9 Answers9

34

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');
omdel
  • 911
  • 1
  • 9
  • 13
  • may not be possible in rails 5 as each form get his own auth token https://blog.bigbinary.com/2016/01/11/per-form-csrf-token-in-rails-5.html – Fabrizio Bertoglio Feb 11 '19 at 18:46
  • 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. – omdel Mar 03 '19 at 03:08
33

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)
Adam Grant
  • 12,477
  • 10
  • 58
  • 65
  • This is the solution for Rails 5+ with the gotcha that [`per_form_csrf_tokens` must be set](https://blog.bigbinary.com/2016/01/11/per-form-csrf-token-in-rails-5.html) for Single Page Application situations. – tadman May 15 '19 at 21:20
33

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

Muhammad Adeel Zahid
  • 17,474
  • 14
  • 90
  • 155
  • 7
    I had to use encodeURIComponent( AUTH_TOKEN ), but using it in the query string worked once I did this. – Ken Sep 27 '14 at 03:29
  • 2
    token should not be sent in the url! – Gacci Oct 25 '17 at 20:06
  • @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. – Julien Oct 24 '20 at 19:55
15

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!

Yavor Ivanov
  • 449
  • 3
  • 8
  • 2
    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. – iblue Aug 03 '16 at 12:53
8

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();
Rustam Gasanov
  • 15,290
  • 8
  • 59
  • 72
3

I just ran into this issue but I tried this approach in my application.js file:

$(document).ajaxSend(function(e, xhr, options) {
  if (options.data == null) {
    options.data = {};
  }
  options.data['authenticity_token'] = token;
});

This is the original question where I got the idea: ajaxSend Question

Community
  • 1
  • 1
carnator
  • 545
  • 1
  • 4
  • 8
3

You could include the AUTH_TOKEN in the form itself, as a hidden input.

<input type="hidden" name="AUTH_TOKEN">1234abcd</input>
BishopRook
  • 1,240
  • 6
  • 11
  • `` (Note that I'm using JSX for templating. Replace the `{` with whatever's appropriate for your template language. – Bryan Larsen Feb 03 '15 at 14:15
2

Here is a solution that applies to all AJAX requests. You add the following to your all.js (or whatever your sitewide javascript file is)

$.ajaxSetup({
    headers: {
        'X-CSRF-Token': document.querySelector('meta[name="csrftoken"]').content
    }
});

This will automatically add the token as a header on all AJAX requests. Works for me in Rails 7.

Patrick G.
  • 697
  • 7
  • 13
0

Simply using form_tag automatically includes CSRF token parameter. Rails supports "Unobtrusive Javascript" meaning that the form will still be submitted via AJAX. Controller actions support "respond_to" block, and you can use .js.erb extension to make changes on the web page in response to form submit.

wondersz1
  • 757
  • 8
  • 15