Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 467
Matt Grande Avatar asked Apr 08 '09 19:04

Matt Grande


2 Answers

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.

like image 53
Ron DeVera Avatar answered Oct 09 '22 02:10

Ron DeVera


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.

like image 22
Darren Greaves Avatar answered Oct 09 '22 02:10

Darren Greaves