Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

AngularJS is not sending-X_XSRF-TOKEN

I am developing an Ionic mobile app to talk to my backend Rails server using a JSON API. I have read that AngularJS will automatically handle XSRF protection by sending a header X-XSRF-TOKEN on the POST request if the first GET request returns a cookie named XSRF-TOKEN

I have updated my Rails application_controller.rb to be as follows:

class ApplicationController < ActionController::Base
  protect_from_forgery
  after_filter :set_access_control_headers
  after_filter :set_csrf_cookie_for_ng

  def after_sign_in_path_for(resource)
    main_path
  end

  def after_sign_out_path_for(resource)
    login_path
  end

  ##
   # Sets headers to support AJAX Cross-Origin Resource Sharing.
   # This is only needed for testing within browser (i.e. mobile apps do not need it).
   ##
  def set_access_control_headers
    # hosts who can make AJAX requests
    headers['Access-Control-Allow-Origin'] = 'http://localhost:8100'
    headers['Access-Control-Request-Method'] = '*'
    headers['Access-Control-Allow-Headers'] = 'accept, content-type, x-xsrf-token'
    # allow clients to use cookies to track session state
    headers['Access-Control-Allow-Credentials'] = 'true'
  end

  ##
   # Sets a cookie containing an XSRF token. This should be returned by the
   # client as a header field named 'X-XSRF-TOKEN'
  def set_csrf_cookie_for_ng
    cookies['XSRF-TOKEN'] = form_authenticity_token if protect_against_forgery?
  end

protected

  def verified_request?
    super || form_authenticity_token == request.headers['X-XSRF-TOKEN']
  end
end

The AngularJS code is:

$http({
  method: 'POST',
  url: $scope.getBackendUrl() + '/reports.json',
  params: params,
  withCredentials: true,
  headers: {
    'Content-Type': 'application/x-www-form-urlencoded'
  }
})

I have taken Wireshark dumps and can see that the Rails server is sending through the Cookie (and I can read this within AngularJS). However, AngularJS is not sending the header X-XSRF-TOKEN to my backend Rails server, causing an WARNING: Can't verify CSRF token authenticity.

I have read through a bunch of SO questions to no avail. E.g. XSRF headers not being set in AngularJS

I have added the CORS header stuff so that I could test from Chrome. Again, I can see the cookie come through from the server, but the header is not being sent back. However, the cookie is being sent back in the 'Cookie' header field.

Can anyone see what I am missing, or things I can try in order to solve this? I am currently parsing the Cookie header field in the request to pull out the token and check authenticity to get around the issue while testing.

def verified_request?
  # should just need to do the below, but for some reason AngularJS is not setting 'X-XSRF-TOKEN'
  #super || form_authenticity_token == request.headers['X-XSRF-TOKEN']
  if(super)
    true
  else
    cookie = request.headers['Cookie'] || ""
    value = cookie.nil? ? "" : CGI.unescape( cookie.gsub(/.*XSRF-TOKEN=(.+);.*/, '\1') )
    form_authenticity_token == value
  end
end

Versions:

  • Rails 3.2.3
  • Ionic 1.1.6 (which bundles in AngularJS)
like image 516
al. Avatar asked Aug 07 '14 02:08

al.


People also ask

Can I use XSRF-token in GET request in angular?

CSRF not designed for GET or HEAD requests. XSRF-TOKEN name because angular expects this name. This is the default if you want another name google how to change the angular default and you will find. Angular default again it expects the cookie to be at the root of the domain. (Guessing)

Why can't I use CSRF in angular?

IT MUST BE A RELATIVE PATH Httponly false because if true, angular or any other JavaScript won't be able to access it. CSRF not designed for GET or HEAD requests. XSRF-TOKEN name because angular expects this name. This is the default if you want another name google how to change the angular default and you will find.

What is the XSRF-token name for cookies in angular?

XSRF-TOKEN name because angular expects this name. This is the default if you want another name google how to change the angular default and you will find. Angular default again it expects the cookie to be at the root of the domain. (Guessing) And no absolute paths because cookies are only sent for the same domain. Cookies are domain binded.

How to get client to read XSRF-token for every POST request?

For every POST request I want my client to read the XSRF-token and set a X-XSRF-TOKEN header to this token. I'll check every request by checking if the request header and the user session XSRF-token match. If they do, I'll also check JWT for authentication if I need. After validating the XSRF-token, I'll make changes to the database.


2 Answers

According to the documentation

$http: The header will not be set for cross-domain requests.

If you have to use CORS, you would also doing cross domain requests. Would still be able to do it yourself with an $httpInterceptor. You first read the cookie value, and then attach the header to the config before the request is fired.

function readCookie(name) {
    var nameEQ = name + "=";
    var ca = document.cookie.split(';');
    for(var i=0;i < ca.length;i++) {
        var c = ca[i];
        while (c.charAt(0)==' ') c = c.substring(1,c.length);
        if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length);
    }
    return null;
}
module.factory('XSRFInterceptor', function() {
    var XSRFInterceptor = {
        request: function(config) {
            var token = readCookie('XSRF-TOKEN');
            if (token) {
                config.headers['X-XSRF-TOKEN'] = token;
            }
            return config;
        }
    };
    return XSRFInterceptor;
}]);
module.config(['$httpProvider', function($httpProvider) {
    $httpProvider.interceptors.push('XSRFInterceptor');
}]);
like image 132
Jeremy Wilken Avatar answered Oct 13 '22 19:10

Jeremy Wilken


Just for anyone who came here like me trying to figure out why Angular is not sending X-XSRF-TOKEN header:

In my case I did not pay attention that I'm sending XSRF-TOKEN cookie with HttpOnly flag.

like image 45
Eugene Avatar answered Oct 13 '22 19:10

Eugene