Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

"WARNING: Can't verify CSRF token authenticity" error - CORS with Devise and :token_authenticatable

I have a single page app that authenticates to another domain using CORS. All the requests are JSON requests.

My app can authenticates OK and can make GET requests OK. Authentication is using token_authenticatable. I.e. all requests append '?auth_token=whatever'

So, my actual problem is that when I try to do a PUT request I get a WARNING: Can't verify CSRF token authenticity message in the rails log as well as a CanCan::AccessDenied (You are not authorized to access this page.) exception.

Simply adding skip_before_filter :verify_authenticity_token to the rails controller fixes the issue.

Therefore I can only conclude that my ajax requests are sending an invalid or empty csrf_token.

I don't really understand how that can be, since I believe I am correctly sending the X-CSRF-Token header correctly with each ajax request.

Basically, my app authenticates and Devise sends back an auth_token and a csrf_token:

render :status => 200, :json => {
  :auth_token => @user.authentication_token,
  :csrf_token => form_authenticity_token
}

I then store those tokens in my ajax app, and using ajaxSend in jQuery, set it up so jQuery passes those tokens with each request:

initialize: ->
  @bindTo $(document), 'ajaxSend', @appendTokensToRequest

appendTokensToRequest: (event, jqXHR, options) ->
  if not @authToken? then return

  if @csrfToken?
    jqXHR.setRequestHeader 'X-CSRF-Token', @csrfToken

  if options.type is 'POST'
    options.data = options.data + (if options.data.match(/\=/) then '&' else '') +
    $.param auth_token:@authToken
  else
    options.url = options.url + (if options.url.match(/\?/) then '&' else '?') +
    $.param auth_token:@authToken

I can then see in the chrome network tab, that for each GET request the auth_token param is being sent, as well as the X-CSRF-Token header.

On PUT requests however it doesn't seem to be working though.

My theory is that CORS is stuffing things up. If you make a CORS request, your browser actually makes an additional OPTIONS request first just to check that you do have permission to access this resource.

I suspect that it is the OPTIONS request which is not passing the X-CSRF-Token header, thus rails immediately invalidates the csrf_token on the rails end. Then when jQuery makes the actual PUT request the csrf_token it passes is no longer valid.

Could this be the problem?

What can I do to prove that? Chrome doesn't seem to show me the OPTIONS requests in the network tab to help me debug the issue.

It's not a major issue, because I can just turn the CSRF stuff off. But I'd like to know why it's not working.

like image 776
asgeo1 Avatar asked Nov 03 '22 13:11

asgeo1


1 Answers

I think you'll need to handle the OPTIONS request, which should respond with the various headers that will allow the CORS request, IIRC they are the access-control-allow-method, access-control-allow-origin and access-control-allow-headers. Because the OPTIONS request is failing, the PUT request probably isn't occurring.

like image 84
jamiebarrow Avatar answered Nov 15 '22 06:11

jamiebarrow