18

I am building an Angular app that interacts with an API built with ASP.NET Web API 2. I am using Basic Authentication by sending an Authorization header with each request that requires authentication:

Angular snippet:

$http.defaults.headers.common['Authorization'] = authHeader;

Request:

Accept:application/json, text/javascript
Accept-Encoding:gzip, deflate, sdch
Accept-Language:en-US,en;q=0.8
Access-Control-Max-Age:1728000
Authorization:Basic [base64 encoded credential couplet here]
Connection:keep-alive
DNT:1
Host: blah.com
Origin:http://localhost:9000
Referer:http://localhost:9000/
User-Agent:Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_3) AppleWebKit/53

This all works OKAY, but a preflight OPTIONS request is sent with every GET or POST request. This is majorly impacting the perceived speed of the application. I have done lots of reading on CORS "Simple Requests" and it seems that in order to avoid the dreaded preflight OPTIONS request is to avoid adding any custom headers in my requests. I've tried lots of other stuff like sending a Content-Type of text/plain, but it seems that the Authorization header is the thing that is violating the CORS "Simple request" requirement.

So it seems that I may have to move the API over to use token based authentication/authorization. In order to avoid preflight requests, it seems that I will need to place the token in the query string. This is okay as it is only a small internal web app which will only be accessed by a couple of users anyway. I intend to implement caching on controller responses. As each request to an controller action will have a different token in the querystring based on the currently authenticated user, will this render cacheing useless?

So:

  1. How do I avoid preflight requests (using custom Authorization headers if at all possible)
  2. If 1.) is not possible, and I move to token based auth, will I be unable to cache API responses for controller actions
  3. What are the most widely used methods to avoid preflight requests but also to auth users securely?

n.b I know there are a couple of other threads on SO and elsewhere on the web regarding this, but none of them tend to provide a definitive answer on whether it is possible to avoid preflight requests for GETs and POSTs when using custom HTTP authorization headers.

adaam
  • 3,616
  • 5
  • 24
  • 51
  • 1
    1.) Is not yet possible across all browsers and HTTP methods: https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS#Preflighted_requests – Tyler May 22 '15 at 19:31
  • 1
    You should be able to use [XMLHttpRequest withCredentials](https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS#Requests_with_credentials) to send a non-Preflighted request with an authorization cookie. – Dave May 22 '15 at 19:39

2 Answers2

9

I think this post (How to apply CORS preflight cache to an entire domain) pretty much says it all - there is not much you can do about this

The one simple solution is to add a reverse proxy to the proxy/webserver serving your angular app (e.g. nginx) to route your RESTful calls via the same domain, e.g. appdomain.com/api --> apidomain.com.

Community
  • 1
  • 1
Reto
  • 2,977
  • 1
  • 17
  • 30
  • 1
    Thanks Reto! Although I would have loved to sorted out the whole preflight thing in a proper way, I eventually opted for this answer. It turns out that you can set up a reverse proxy in IIS and in an Azure website so my client will also be hosted in an Azure web app with forwarding of local `/api` requests onto the other Azure web app hosting the API. Reference: http://ruslany.net/2014/05/using-azure-web-site-as-a-reverse-proxy/ – adaam May 23 '15 at 12:40
3

Another solution that seems to be working OK for me. Instead of setting up a proxy and needing to route to the same domain, it is possible to return the preflight request directly from nginx and therefore reducing the time required by the preflight request down to just a couple of milliseconds.

Here is a simple snippet that can be used with nginx.

 location / {
    if ($request_method = 'OPTIONS') {
        add_header 'Access-Control-Allow-Origin' '*';
        add_header 'Access-Control-Allow-Credentials' 'true';
        add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
        add_header 'Access-Control-Allow-Headers' 'Content-Type, Authorization';
        add_header 'Access-Control-Max-Age' 1728000;
        add_header 'Content-Type' 'text/plain charset=UTF-8';
        add_header 'Content-Length' 0;
        return 204;
     }
}

Once the preflight request is successful, it is then possible to simple add "Access-Control-Allow-Origin" and other necessary stuff to the 'GET','POST' requests, etc...

andy_roddam
  • 373
  • 3
  • 10