Rails Can't verify CSRF token authenticity with ember-data

I am using the current beta channel’s ember-data (1.0.0-beta6) in a rails project. Anytime, I post/put/delete data, I get a CSRF exception in rails 4. Obviously, this is because authenticity_token is not be sent in the request. I have the CSRF meta tag with the correct authenticity_token available on the page.

I can bypass the error in my rails controller by adding skip_before_filter :verify_authenticity_token, but I’d prefer to take advantage of the CSRF token check if I can.

I’m guessing there is a way to insert the authenticity_token by doing some magic in:

App.Store = DS.Store.extend({
    adapter: '-active-model',
    magic_goes_here: '...'
})  

I know that I can grab the authenticity_token with jQuery("meta[name='csrf-token']").attr("content")

How do I get ember-data to inject authenticity_token into non-GET requests?

add this function, after defining the store.

$(function() {
    var token = $('meta[name="csrf-token"]').attr('content');
    return $.ajaxPrefilter(function(options, originalOptions, xhr) {
        return xhr.setRequestHeader('X-CSRF-Token', token);
    });
});
4 Likes

Another option, extend the rest adapter and override the ajax hook to build out the jQuery.ajax.

hash.beforeSend = function(xhr) { xhr.setRequestHeader('X-CSRF-Token', token); }

https://github.com/emberjs/data/blob/f44952a9c9c5b7ec6183d5d86a567f9ae307d039/packages/ember-data/lib/adapters/rest_adapter.js#L564

@mrinterweb check @amajdee suggestion, Ember Appkit Rails does it like that, check https://github.com/dockyard/ember-appkit-rails/blob/master/lib/generators/templates/csrf.js

2 Likes

Thank you @amajdee. That worked perfectly. I converted it to CoffeeScript should that help anyone.

$(->
  token = $('meta[name="csrf-token"]').attr('content')
  $.ajaxPrefilter((options, originalOptions, xhr)->
    xhr.setRequestHeader('X-CSRF-Token', token)
  )
)

@jasonmit I try to avoid monkey patching when possible, and extending the ajax function to insert the header seems more brittle in the event that ember-data changes the function’s implementation. Thank you for the suggestion. I always appreciate alternative approaches.

I hear you, but I’ve found it nearly impossible to not extend ember-data’s adapter and serializer. While looking at the adapter again, the better place for this would be in ajaxOptions (line 590) where you can extend the hash object with what I mentioned above and simply call this_super.apply(this, arguments); to avoid any tracking issues with ember-data in the future.

The reason I’d prefer this approach is you’re not overriding all jQuery-based ajax requests and the code is where it belongs, not floating around in your app somewhere.

here’s another option:

DS.ActiveModelAdapter.reopen({
  init: function() {
    this._super();
    var token = $('meta[name="csrf-token"]').attr('content');

    this.headers = {
      'X-CSRF-Token': token
    }
  }
});

One big reason that I use $.ajaxPrefilter for things like this is that I want all requests to my server to get the same behaviour anyway. Unless I’m missing something most apps probably do things other than CRUD and use $.ajax directly here and there.

By looking through the code to rest_adapter, this is the suggested way.

https://github.com/emberjs/data/blob/master/packages/ember-data/lib/adapters/rest_adapter.js#L87-L100

That can easily become:

var token = $('meta[name="csrf-token"]').attr('content');
DS.RESTAdapter.reopen({
    headers: {
        "X-CSRF-Token": token
    }
});

This approach also works even though I am using the “active-model” adapter.

3 Likes

I have an ember-cli project and I am trying to implement the CSRF token authentication to connect to my Rails backend. I have tried everything in this post and have had no luck. I have tried adding the function in various places (script in main index.html, within Active Model Adapter, inside of app.js, etc) and I continue to get Uncaught SyntaxError: Unexpected token < in my console and the following rails errors:

Can't verify CSRF token authenticity
Completed 422 Unprocessable Entity in 2ms

ActionController::InvalidAuthenticityToken (ActionController::InvalidAuthenticityToken):

Where is the appropriate place to implement CSRF in an ember-cli project?

1 Like

rwjblue from #ember-cli IRC pointed me to rails-csrf by abuiles

I’ve previously solved this by just adding back //= require jquery_ujs to my application.js…