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)