Matt Grande
Matt Grande

Reputation: 12157

protect_from_forgery & Unobtrusive Javascript

I have some javascript making an ajax call in my Rails site:

$.ajax({type: "PUT", url: url, data: { dummy: data }, complete: function(data) {}});

When Rails gets it, it throws back an ActionController::InvalidAuthenticityToken Error. I'd like to keep the protect_from_forgery stuff in there, if possible... But I'm at a loss for how can I pass the auth token from a javascript file?

Can anyone help me out?

Upvotes: 13

Views: 2882

Answers (2)

Ron DeVera
Ron DeVera

Reputation: 14644

In your layout, add this before any other JS runs:

<script>
  function authToken() {
    return '<%= form_authenticity_token if protect_against_forgery? -%>';
  }
</script>

authToken is coded as a function so that it's less likely you'll accidentally overwrite it with other JavaScript.

Alternatively, as of Rails 3, the auth token is embedded as a <meta> tag, which you can read with:

<script>
  function authToken() {
    return $('meta[name="csrf-token"]').attr('content');
  }
</script>

In your main JS, you can then call authToken(), and it'll return your authenticity token as a string to include in your Ajax calls. For example, using jQuery:

$.ajax({
  type: 'PUT',
  url:  url,
  data: {
    foo: bar,
    authenticity_token: authToken()
  },
  complete: function(data) {}
});

Note that if you use Rails' built-in form_for helper, it automatically adds the authenticity token in a hidden input. If you want to send all of the form's data, including the hidden auth token, you can simply use:

var $form = $('form');
$.ajax({
  url:      $form.attr('action'),
  type:     $form.attr('method'),
              // "get" or "post"; overridden by Rails' hidden "_method"
              // input value, e.g., "put"
  data:     $form.serialize(),
              // Includes hidden "authenticity_token" and "_method" inputs
  complete: function(data) {}
});

This pattern is often useful when you've already written a form that works without JS, and you're adding an unobtrusive layer of JS that simply sends the form's data via Ajax.

Upvotes: 21

Darren Greaves
Darren Greaves

Reputation: 3344

Thanks for the above solution.
I am using some standard forms in my site that don't use the rails form_tag so instead I simply added it as a hidden form element.

<form action="...">
    <%= hidden_field_tag 'authenticity_token', form_authenticity_token if protect_against_forgery? %>
    ... rest of form...
</form>

Works a treat.

Upvotes: 3

Related Questions