57

I'm using backbone.js and it works great. but the forms I'm creating as a javascript template lacks the rails csrf protection token. How do I add it to templates I'm creating in javascript?

CamelCamelCamel
  • 5,200
  • 8
  • 61
  • 93

5 Answers5

87

Best way I solved this, inside the form:

<%= hidden_field_tag :authenticity_token, form_authenticity_token %>

Update:

It looks like the form_authenticity_token is private for controllers in the newer rails versions.

If that's the case for you, what I suggest is: declare a variable in a controller like: @form_token = form_authenticity_token and use it in the view you are looking for.

peterh
  • 11,875
  • 18
  • 85
  • 108
lucianosousa
  • 8,144
  • 4
  • 21
  • 25
  • It works, but why? Isn't [`form_authenticity_token`](http://apidock.com/rails/ActionController/RequestForgeryProtection/form_authenticity_token) private in the controller? – Franklin Yu Aug 28 '16 at 01:30
  • This doesnt work for me Rails 4.2.2. I get: `undefined local variable or method 'form_authenticity_token' for #<#:0x007ff7eec39b58>` – juliangonzalez Aug 29 '16 at 21:04
  • Hey guys, looks like `form_authenticity_token` is private for controllers like Franklin said. What I saw as a suggestion was declare a variable in a controller `@form_token = form_authenticity_token` and use it in the view. – lucianosousa Sep 01 '16 at 19:38
  • This works for me in rails 4.2.5. Note: this form was generated in ERB and not in JS. – Jared Menard Oct 24 '17 at 18:53
38

If you have <%= csrf_meta_tag %> in your layout somewhere and that is accessible to you from the js, then you can access it using $('meta[name="csrf-token"]')

See http://eunikorn.blogspot.com/2011/07/working-with-backbonejs-in-harmony-with.html for an idea on how to hack in csrf support into each backbone request

Thong Kuah
  • 3,263
  • 2
  • 19
  • 29
  • 1
    See https://gist.github.com/3482636 for another example of how this can be hacked in. I have no idea if it's the same. – Chris Pfohl Sep 19 '12 at 15:54
  • 3
    Specifically: `$.ajax(data: {authenticity_token: $('meta[name="csrf-token"]').attr("content")})` – Meekohi Aug 30 '18 at 19:06
7

I have a form inside a Vue component in a Rails 6 app.

To my surprise, it was sufficient to include a hidden input with the name authenticity_token within the Vue template and on page load, Rails filled out the value with a CSRF protection token.

E.g.

<template>
  <div id="app">
    <form
      action="/submit"
      method="post"
      @submit.prevent="onSubmit"
    >
      <input
        type="hidden"
        name="authenticity_token"
        value=""
      >
      <!-- rest of form -->
    </form>
  </div>
</template>

Which gets rendered as:

<div id="app">
  <form action="/submit" method="post">
    <input type="hidden" name="authenticity_token" value="zl9PJiE...">
    ...
  </form>
</div>
James Hibbard
  • 16,490
  • 14
  • 62
  • 74
  • 1
    thank you, I had to do this to fix a similar issue where using a turbo stream in rails removed the authenticity token from the DOM – lyf1n Jul 11 '22 at 17:07
  • Glad it helped :) What version of Rails are you using? This seems to no longer work in in v7. – James Hibbard Jul 12 '22 at 10:24
  • Actually using rails 7 (7.0.2.4 to be precise). What exactly doesn't seem to work any more? I'm using a stimulus controller to set the csrf authenticity token on page load. – lyf1n Jul 15 '22 at 02:03
  • In Rails < v7, it was sufficient to just include an empty input tag and Rails would do the rest. In Rails >= v7, Rails no longer automatically populates the value attribute with a valid token. – James Hibbard Aug 03 '22 at 08:43
4

You can prepend the csrf token to every form that uses 'post' or 'delete'. Here it is in coffeescript:

$ -> 
  for f in $("form")
    if f.method == 'post' or f.method == 'delete'
      $(f).prepend("<input type='hidden' name='authenticity_token' value='" + token + "'>")

Make sure you have <%= csrf_meta_tags %> in your layout. It should already be in the standard 'application' layout, but add it if you're using a different layout.

suga_shane
  • 694
  • 10
  • 19
  • 4
    Where is `token` declared? – juliangonzalez Aug 29 '16 at 20:54
  • What @suga_shane means by writing, "make sure you have `<%= csrf_meta_tags %>`", is that this Rails helper generates a token for you and inserts it in the HTML `head`. The token is the `content` attribute in the `` tag with name `csrf-token`. – sameers Jul 28 '17 at 20:54
1

As for Rails 4.2.2 you are not allowed to use

<%= hidden_field_tag :authenticity_token, form_authenticity_token %>

from your .js.erb assets file.

However You can create the form inside the .js.erb file and in the view containing the form .html.erb file use the hidden_field_tag helper to generate the token element. As this element is going to be generated outside the form you can use jquery to append this element to the form.

Case of study: SweetAlert (first version, version too seems to have solved this problem)

show.js.erb

$('.js-button-apply-offer').click(function(e) {
var urlOffer = $(this).attr('data-url-offer');
var modalParams = {
    type: 'warning',
    title: 'add file',
    text: '<p>Need to add a file before continuing</p>' // This is a hack for Sweet alert, solved in SweetAlert2 Consider upgrade
    +"<form action='"+urlOffer+"' id='formCustomCV' method='post' enctype='multipart/form-data' data-remote='true'>"
    + "<input type='file' name='custom_cv' id='fileToUploadAlert' accept='application/pdf'>\n"
    +"</form>",
    html: true,
    showCancelButton: true,
    confirmButtonColor: '#DD6B55',
    confirmButtonText: 'Send',
    cancelButtonText: 'Cancel',
    closeOnConfirm: false
  }
swal(modalParams,
function(){
  var form_token = $('#form_token');
  $('#formCustomCV').append(form_token).submit(); //update to submit using ajax
});

show.html.erb

<%= button_tag t('offers.offer.apply'),
  class: 'center-block btn btn-success js-button-apply-offer',
  id: "js-button-apply-offer",
  data: {
    url_offer: apply_talents_offer_path(@offer),
  } 
%>
<%= hidden_field_tag :authenticity_token, form_authenticity_token, id: :form_token %>
juliangonzalez
  • 4,231
  • 2
  • 37
  • 47