WARNING: Can't verify CSRF token authenticity rails
Asked Answered
L

19

257

I am sending data from view to controller with AJAXand I got this error:

WARNING: Can't verify CSRF token authenticity

I think I have to send this token with data.

Does anyone know how can I do this ?

Edit: My solution

I did this by putting the following code inside the AJAX post:

headers: {
  'X-Transaction': 'POST Example',
  'X-CSRF-Token': $('meta[name="csrf-token"]').attr('content')
},
Leaper answered 26/8, 2011 at 10:23 Comment(6)
do you have <%= csrf_meta_tag %> in your layout header?Divisibility
yes like this : <%= csrf_meta_tags %>Leaper
do you have jquery-rails libraries that provide ajax client-side functionality?Divisibility
And the HAML way is to add "= csrf_meta_tags"Sydel
What does the 'X-Transaction' line do? Seems to work fine without it.Hindermost
May I ask you if you can answer this very similar question? #50160347Descender
O
409

You should do this:

  1. Make sure that you have <%= csrf_meta_tag %> in your layout

  2. Add beforeSend to all the ajax request to set the header like below:


$.ajax({ url: 'YOUR URL HERE',
  type: 'POST',
  beforeSend: function(xhr) {xhr.setRequestHeader('X-CSRF-Token', $('meta[name="csrf-token"]').attr('content'))},
  data: 'someData=' + someData,
  success: function(response) {
    $('#someDiv').html(response);
  }
});

To send token in all requests you can use:

$.ajaxSetup({
  headers: {
    'X-CSRF-Token': $('meta[name="csrf-token"]').attr('content')
  }
});
Ofay answered 17/11, 2011 at 23:42 Comment(7)
The jQuery UJS library provided by the Rails team adds the CSRF token to jQuery AJAX request automatically. The README contains instructions on how to get setup. github.com/rails/jquery-ujs/blob/master/src/rails.js#L91Dartmoor
Note that you can set the header for all requests at once with the $.ajaxSetup function.Pause
Excellent! Been searching for a while for this answer. Works seamlessly. Thanks!Gelatinize
@JamesConroy-Finn This stopped working in Rails 4. Still trying to figure out why.Howarth
Unfortunatly, this solution doesn't work in Rails 4.2.x.Expedition
As a note, if you are using jQuery UJS as suggested above, you need to ensure that the rails-ujs include comes after the jquery include or it will fail with the same error as the op.Thump
May I ask you if you can answer this very similar question? #50160347Descender
E
31

The best way to do this is actually just use <%= form_authenticity_token.to_s %> to print out the token directly in your rails code. You dont need to use javascript to search the dom for the csrf token as other posts mention. just add the headers option as below;

$.ajax({
  type: 'post',
  data: $(this).sortable('serialize'),
  headers: {
    'X-CSRF-Token': '<%= form_authenticity_token.to_s %>'
  },
  complete: function(request){},
  url: "<%= sort_widget_images_path(@widget) %>"
})
Exertion answered 25/11, 2011 at 10:15 Comment(4)
Instead of doing this for each ajax command, you could add headers to $.ajaxSetup().Sofko
I'd rather recommend using this answer...Poppied
I don't really like the approach of using ERB in the javascript.Trellis
This forces you to generate your javascript with ERB, which is very limiting. Even if there are places where ERB might be a good fit, there are others where it's not, and adding it just to get the token would be a waste.Custos
C
22

If I remember correctly, you have to add the following code to your form, to get rid of this problem:

<%= token_tag(nil) %>

Don't forget the parameter.

Chaiken answered 26/8, 2011 at 10:34 Comment(1)
Actually, this should be: <%= token_tag(nil) %>. Then you get the auto-generated token.Infralapsarian
P
15

Indeed simplest way. Don't bother with changing the headers.

Make sure you have:

<%= csrf_meta_tag %> in your layouts/application.html.erb

Just do a hidden input field like so:

<input name="authenticity_token" 
               type="hidden" 
               value="<%= form_authenticity_token %>"/>

Or if you want a jQuery ajax post:

$.ajax({     
    type: 'POST',
    url: "<%= someregistration_path %>",
    data: { "firstname": "text_data_1", "last_name": "text_data2", "authenticity_token": "<%= form_authenticity_token %>" },                                                                                  
    error: function( xhr ){ 
      alert("ERROR ON SUBMIT");
    },
    success: function( data ){ 
      //data response can contain what we want here...
      console.log("SUCCESS, data="+data);
    }
});
Palmette answered 6/5, 2013 at 12:58 Comment(2)
Adding the hidden input field to my input form solved the problem for me.Dogeared
this is done automatically if you use Rails' form helpers.Prichard
F
13

Ugrading from an older app to rails 3.1, including the csrf meta tag is still not solving it. On the rubyonrails.org blog, they give some upgrade tips, and specifically this line of jquery which should go in the head section of your layout:

$(document).ajaxSend(function(e, xhr, options) {
 var token = $("meta[name='csrf-token']").attr("content");
  xhr.setRequestHeader("X-CSRF-Token", token);
});

taken from this blog post: http://weblog.rubyonrails.org/2011/2/8/csrf-protection-bypass-in-ruby-on-rails.

In my case, the session was being reset upon each ajax request. Adding the above code solved that issue.

Fionnula answered 10/1, 2012 at 4:48 Comment(0)
T
10
  1. Make sure that you have <%= csrf_meta_tag %> in your layout
  2. Add a beforeSend to include the csrf-token in the ajax request to set the header. This is only required for post requests.

The code to read the csrf-token is available in the rails/jquery-ujs, so imho it is easiest to just use that, as follows:

$.ajax({
  url: url,
  method: 'post',
  beforeSend: $.rails.CSRFProtection,
  data: {
    // ...
  }
})
Thereof answered 8/7, 2015 at 11:47 Comment(3)
Simple without re-implementing what's already included by Rails. This should be the selected answer.Dubose
In Rails 5.1 also works: headers: { 'X-CSRF-Token': Rails.csrfToken() }Cambodia
@nathanvda, Maybe you can answer this similar question: #50160347Descender
R
7

The top voted answers here are correct but will not work if you are performing cross-domain requests because the session will not be available unless you explicitly tell jQuery to pass the session cookie. Here's how to do that:

$.ajax({ 
  url: url,
  type: 'POST',
  beforeSend: function(xhr) {
    xhr.setRequestHeader('X-CSRF-Token', $('meta[name="csrf-token"]').attr('content'))
  },
  xhrFields: {
    withCredentials: true
  }
});
Rebatement answered 23/11, 2014 at 0:32 Comment(1)
May I ask you if you can answer this very similar question? #50160347Descender
D
6

I just thought I'd link this here as the article has most of the answer you're looking for and it's also very interesting

http://www.kalzumeus.com/2011/11/17/i-saw-an-extremely-subtle-bug-today-and-i-just-have-to-tell-someone/

Damsel answered 18/11, 2011 at 0:11 Comment(0)
O
6

oops..

I missed the following line in my application.js

//= require jquery_ujs

I replaced it and its working..

======= UPDATED =========

After 5 years, I am back with Same error, now I have brand new Rails 5.1.6, and I found this post again. Just like circle of life.

Now what was the issue is: Rails 5.1 removed support for jquery and jquery_ujs by default, and added

//= require rails-ujs in application.js

It does the following things:

  1. force confirmation dialogs for various actions;
  2. make non-GET requests from hyperlinks;
  3. make forms or hyperlinks submit data asynchronously with Ajax;
  4. have submit buttons become automatically disabled on form submit to prevent double-clicking. (from: https://github.com/rails/rails-ujs/tree/master)

But why is it not including the csrf token for ajax request? If anyone know about this in detail just comment me. I appreciate that.

Anyway I added the following in my custom js file to make it work (Thanks for other answers to help me reach this code):

$( document ).ready(function() {
  $.ajaxSetup({
    headers: {
      'X-CSRF-Token': Rails.csrfToken()
    }
  });
  ----
  ----
});
Observation answered 25/12, 2013 at 13:42 Comment(3)
This was what I needed to do as well. The reason this came up is because I'm building a React App to slowly replace an existing Rails App. Since there's a bunch of javascript noise going on in the existing app, I created a different layout that accesses a different javascript file, but I failed to include jquery_ujs. That was the trick.Julesjuley
Yes, Sometimes If we miss that when rework, refactor something..Its hard to find whats going wrong. Because we are not doing anything manually to make it work, Rails automatically including it. We think it already there. Thanks for such social Qn / Ans SitesObservation
Maybe you can answer this similar question: #50160347Descender
T
6

You can write it globally like below.

Normal JS:

$(function(){

    $('#loader').hide()
    $(document).ajaxStart(function() {
        $('#loader').show();
    })
    $(document).ajaxError(function() {
        alert("Something went wrong...")
        $('#loader').hide();
    })
    $(document).ajaxStop(function() {
        $('#loader').hide();
    });
    $.ajaxSetup({
        beforeSend: function(xhr) {xhr.setRequestHeader('X-CSRF-Token', $('meta[name="csrf-token"]').attr('content'))}
    });
});

Coffee Script:

  $('#loader').hide()
  $(document).ajaxStart ->
    $('#loader').show()

  $(document).ajaxError ->
    alert("Something went wrong...")
    $('#loader').hide()

  $(document).ajaxStop ->
    $('#loader').hide()

  $.ajaxSetup {
    beforeSend: (xhr) ->
      xhr.setRequestHeader('X-CSRF-Token', $('meta[name="csrf-token"]').attr('content'))
  }
Ty answered 8/4, 2014 at 9:28 Comment(0)
O
6

If you are not using jQuery and using something like fetch API for requests you can use the following to get the csrf-token:

document.querySelector('meta[name="csrf-token"]').getAttribute('content')

fetch('/users', {
  method: 'POST',
  headers: {
    'Accept': 'application/json',
    'Content-Type': 'application/json',
    'X-CSRF-Token': document.querySelector('meta[name="csrf-token"]').getAttribute('content')},
    credentials: 'same-origin',
    body: JSON.stringify( { id: 1, name: 'some user' } )
    })
    .then(function(data) {
      console.log('request succeeded with JSON response', data)
    }).catch(function(error) {
      console.log('request failed', error)
    })
Ofeliaofella answered 5/9, 2016 at 2:45 Comment(1)
May I ask you if you can answer this very similar question? #50160347Descender
M
4

Use jquery.csrf (https://github.com/swordray/jquery.csrf).

  • Rails 5.1 or later

    $ yarn add jquery.csrf
    
    //= require jquery.csrf
    
  • Rails 5.0 or before

    source 'https://rails-assets.org' do
      gem 'rails-assets-jquery.csrf'
    end
    
    //= require jquery.csrf
    
  • Source code

    (function($) {
      $(document).ajaxSend(function(e, xhr, options) {
        var token = $('meta[name="csrf-token"]').attr('content');
        if (token) xhr.setRequestHeader('X-CSRF-Token', token);
      });
    })(jQuery);
    

Mildamilde answered 31/3, 2017 at 10:55 Comment(1)
In 5.1 using webpack, //= require jquery.csrf isn't going to work, right?. Instead I used pack js file with import 'jquery.csrf' in it. Note you do not need to include this with a pack tag in your views.Pyrrho
B
3

If you're using javascript with jQuery to generate the token in your form, this works:

<input name="authenticity_token" 
       type="hidden" 
       value="<%= $('meta[name=csrf-token]').attr('content') %>" />

Obviously, you need to have the <%= csrf_meta_tag %> in your Ruby layout.

Bosco answered 25/4, 2012 at 19:52 Comment(0)
A
3

I struggled with this issue for days. Any GET call was working correctly, but all PUTs would generate a "Can't verify CSRF token authenticity" error. My website was working fine until I had added a SSL cert to nginx.

I finally stumbled on this missing line in my nginx settings:

location @puma { 
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 
    proxy_set_header Host $http_host; 
    proxy_redirect off;
    proxy_set_header X-Forwarded-Proto https;   # Needed to avoid 'WARNING: Can't verify CSRF token authenticity'
    proxy_pass http://puma; 
}

After adding the missing line "proxy_set_header X-Forwarded-Proto https;", all my CSRF token errors quit.

Hopefully this helps someone else who also is beating their head against a wall. haha

Adalineadall answered 5/2, 2018 at 22:7 Comment(0)
C
1

if someone needs help related with Uploadify and Rails 3.2 (like me when I googled this post), this sample app may be helpful: https://github.com/n0ne/Uploadify-Carrierwave-Rails-3.2.3/blob/master/app/views/pictures/index.html.erb

also check the controller solution in this app

Carmarthenshire answered 12/1, 2013 at 22:38 Comment(0)
R
1

For those of you that do need a non jQuery answer you can simple add the following:

xmlhttp.setRequestHeader('X-CSRF-Token', $('meta[name="csrf-token"]').attr('content'));

A very simple example can be sen here:

xmlhttp.open("POST","example.html",true);
xmlhttp.setRequestHeader('X-CSRF-Token', $('meta[name="csrf-token"]').attr('content'));
xmlhttp.send();
Refrigeration answered 29/10, 2013 at 2:24 Comment(2)
Does this not use jQuery for selectors?Shirlyshiroma
xmlhttp.setRequestHeader('X-CSRF-Token', document.querySelector('meta[name="csrf-token"]').content);Vinous
I
0

I'm using Rails 4.2.4 and couldn't work out why I was getting:

Can't verify CSRF token authenticity

I have in the layout:

<%= csrf_meta_tags %>

In the controller:

protect_from_forgery with: :exception

Invoking tcpdump -A -s 999 -i lo port 3000 was showing the header being set ( despite not needing to set the headers with ajaxSetup - it was done already):

X-CSRF-Token: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
X-Requested-With: XMLHttpRequest
DNT: 1
Content-Length: 125
authenticity_token=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

In the end it was failing because I had cookies switched off. CSRF doesn't work without cookies being enabled, so this is another possible cause if you're seeing this error.

Introductory answered 12/11, 2015 at 9:53 Comment(1)
it's very helpfu!Brachyuran
B
0

After upgrading to Rails 7 in 2023, I had this issue for some reason. Perhaps I'm using normal jQuery instead of some rails variant of jQuery, or whatever "UJS" is.

Fixed it by adding this to my page-specific Javascript code:

    // Add the CSRF token to all ajax requests                                                                                                                      
    $.ajaxSetup({                                                                                                                                                   
      headers: {                                                                                                                                                    
        'X-CSRF-Token': $('meta[name="csrf-token"]').attr('content')                                                                                                
      }                                                                                                                                                             
    });                                                                                                                                                             

(I already had <%= csrf_meta_tags %> at the top of my html layout)

My normal jQuery Ajax code looks like:

      var jqxhr = $.post( "something/example", { color: 'red' })
        .done(function(data) {
          console.log( "Ajax success:");
          console.log( data );
        })
        .fail(function(data) {
          console.log( "Ajax error:");
          console.log( data );
        })
        .always(function() {
          console.log( "Ajax finished" );
        });

Blodget answered 2/10, 2023 at 11:17 Comment(0)
D
0

CSRF token is missing while making API call. You can send ""X-CSRF-Token"" in the headers. [name=""csrf-token""] is the html element where you can get the token.

Devil answered 19/2 at 11:4 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.