18

I am working on an app, where server and the api-consuming client reside under different domains, so I would like to use CORS. To do so, I have to set corresponding http headers in the server response:

def cors_set_access_control_headers
  headers['Access-Control-Allow-Origin'] = 'http://localhost'
  headers['Access-Control-Allow-Methods'] = 'POST, GET, OPTIONS'
  headers['Access-Control-Allow-Headers'] = '*, X-Requested-With, X-Prototype-Version, X-CSRF-Token, Content-Type'
  headers['Access-Control-Max-Age'] = "1728000"
end

This method is used as a before_filter in ApplicationController.

For some resources the user has to be authenticated and authorized. Requests are done via XHR/Ajax. So if the user is not authenticated Devise will send a 401 response to the client, instead of redirecting to a sign in page. But the filter to set the CORS headers is not used for that response. Thus the 401 response is not sent to the client. I want to catch and use the 401 response in the client.

Currently I am using a workaround by not using the Devise authentication methods, but a custom auth snippet:

def authenticate_cors_user
  if request.xhr? && !user_signed_in?
    error = { :error => "You must be logged in." }
    render params[:format].to_sym => error, :status => 401
  end
end

This is set as a before_filter in ApplicationController, too. This way the filter to set CORS headers gets triggered and everything works fine.

I would prefer to use the default behaviour of Devise, but the CORS headers would have to be set in the 401 response. How to do this? Do I have to configure warden for that?

How could the CORS headers be set for the 401 response generated by Devise instead of creating my own response?

Nils Blum-Oeste
  • 5,608
  • 4
  • 25
  • 26
  • Still I do not have a solution for that. To give some more context, check out my blog post about how I am using this: http://nils-blum-oeste.net/cors-api-with-oauth2-authentication-using-rails-and-angularjs – Nils Blum-Oeste Aug 02 '12 at 09:42

2 Answers2

21

I successfully used the rack-cors gem https://github.com/cyu/rack-cors and outlined my experience on my blog.

Punchline: Specify middleware order so cors handler is before warden:

config.middleware.insert_before Warden::Manager, Rack::Cors
Lucio
  • 4,753
  • 3
  • 48
  • 77
Mark Nadig
  • 4,901
  • 4
  • 37
  • 46
  • Your approach works great, thanks! However, strangely, it took some time and some additional research to realize, that I really have to change only one line of code in configuration to make `Devise` and `rack-cors` to work together. – denis.peplin Jan 13 '14 at 09:41
  • Hey @digger69. Did you have problems with other exceptions? For example: if I throw an exception in my action `def index; raise 'error'; end` - Rails doesn't return the CORS headers (rack-cors). Does it work for you? – Pablo Cantero Feb 07 '14 at 00:16
  • 4
    PS: You could also use `config.middleware.insert 0, Rack::Cors` to guarantee that `Rack::Cors` will be inserted before any other possible middleware. – Pablo Cantero Feb 07 '14 at 00:23
0

We generally disliked CORS headers. We prefer using HAProxy to to redirects.

With HAProxy you can set up all requests coming to website.com/api/ to be internally redirected to your rails machine.

frontend main *:80
  acl api      path_beg  -i /api
  acl app      path_beg  -i /app
backend app_backend
  reqrep    ^([^\ ]*)\ /app/(.*)  \1\ /\2
  balance   roundrobin
  server    app_files smartphoneapp.localhost:8000
backend api_backend
  reqrep    ^([^\ ]*)\ /api/(.*)  \1\ /\2
  balance   roundrobin
  server    app_api localhost:3000
Hendrik
  • 4,849
  • 7
  • 46
  • 51
  • CORS could be circumvented using a proxy, sure. But still there must be a way to handle this nicely with Devise/warden, I guess. – Nils Blum-Oeste Jun 27 '12 at 18:52