Rails CSRF Protection + Angular.js: protect_from_forgery makes me to log out on POST Rails CSRF Protection + Angular.js: protect_from_forgery makes me to log out on POST ruby-on-rails ruby-on-rails

Rails CSRF Protection + Angular.js: protect_from_forgery makes me to log out on POST


I think reading CSRF-value from DOM is not a good solution, it's just a workaround.

Here is a document form angularJS official website http://docs.angularjs.org/api/ng.$http :

Since only JavaScript that runs on your domain could read the cookie, your server can be assured that the XHR came from JavaScript running on your domain.

To take advantage of this (CSRF Protection), your server needs to set a token in a JavaScript readable sessioncookie called XSRF-TOKEN on first HTTP GET request. On subsequentnon-GET requests the server can verify that the cookie matchesX-XSRF-TOKEN HTTP header

Here is my solution based on those instructions:

First, set the cookie:

# app/controllers/application_controller.rb# Turn on request forgery protectionprotect_from_forgeryafter_action :set_csrf_cookiedef set_csrf_cookie  cookies['XSRF-TOKEN'] = form_authenticity_token if protect_against_forgery?end

Then, we should verify the token on every non-GET request.
Since Rails has already built with the similar method, we can just simply override it to append our logic:

# app/controllers/application_controller.rbprotected    # In Rails 4.2 and above  def verified_request?    super || valid_authenticity_token?(session, request.headers['X-XSRF-TOKEN'])  end  # In Rails 4.1 and below  def verified_request?    super || form_authenticity_token == request.headers['X-XSRF-TOKEN']  end


If you're using the default Rails CSRF protection (<%= csrf_meta_tags %>), you can configure your Angular module like this:

myAngularApp.config ["$httpProvider", ($httpProvider) ->  $httpProvider.defaults.headers.common['X-CSRF-Token'] = $('meta[name=csrf-token]').attr('content')]

Or, if you're not using CoffeeScript (what!?):

myAngularApp.config([  "$httpProvider", function($httpProvider) {    $httpProvider.defaults.headers.common['X-CSRF-Token'] = $('meta[name=csrf-token]').attr('content');  }]);

If you prefer, you can send the header only on non-GET requests with something like the following:

myAngularApp.config ["$httpProvider", ($httpProvider) ->  csrfToken = $('meta[name=csrf-token]').attr('content')  $httpProvider.defaults.headers.post['X-CSRF-Token'] = csrfToken  $httpProvider.defaults.headers.put['X-CSRF-Token'] = csrfToken  $httpProvider.defaults.headers.patch['X-CSRF-Token'] = csrfToken  $httpProvider.defaults.headers.delete['X-CSRF-Token'] = csrfToken]

Also, be sure to check out HungYuHei's answer, which covers all the bases on the server rather than the client.


The angular_rails_csrf gem automatically adds support for the pattern described in HungYuHei's answer to all your controllers:

# Gemfilegem 'angular_rails_csrf'