Background
(If you are familiar with CORS, you might skip to the Question at the end)
CORS [1] is a solution to allow browsers to permit access to resources from different domains. E.g. REST data backends using AJAX.
It is well observed on the Internet that Chrome (and Webkit friends) do not handle HTTP authentication prompts when such a resource demands it. E.g. [2] ( this is to prevent the browser showing an auth login dialog on for example, a Web-Mail sign-in page when an image tags load fails with a 401 and needs credentials. The user may be duped in to entering their webmail credentials)
Chrome's behaviour in this situation is to drop the response, cancelling the request almost silently.
In fact, Chrome will only let the response through normally if a 200 occurs; any other status code is swallowed and the AJAX call is aborted.
If one uses jQuery to perform an Ajax request, and the remote site responds with an HTTP 401 status code, the request is cancelled and the ajax completes with an error, but the 401 code is missing. It as if the connection died or a equivalent lower layer problem.
The only website I've seen which mentions this caveat of CORS specifically is [1]
Browers don't do a good job of reporting what went wrong when there is an error. For example, Firefox reports a status of 0 and an empty statusText for all errors. Browsers also report an error message to the console log, but this message cannot be accessed from JavaScript. When handling onerror, you will know that an error occurred, but not much else.
You can disable this security by using a chrome startup flag - just to test both the backend is functioning 'normally' and one sanity hasn't been unduly affected by the loss of proper status codes. See [2]
Problem
What if the backend and front-end client code is supposed to use the 401 as part of it's authentication process? - any failures are just just mangled into one "error" output.
For example: Upon issuing a request to authenticate - e.g. 2-legged OAuth token request -against a RESTful backend - which uses proper status codes - The client has no idea if:
A backend of ours which was written quite RESTfully, following as many best practices as was deemed cost effective at the time; one of these was relying on proper HTTP status codes. The Java REST Client we have working against has worked great.
Now I have begun creating a Javascript client, for embedding in webpages - specifically using CORS, and we have hit the problem in Chrome etc. (Note Firefox seems fine, and we generally don't care for IE at present.)
Question
What have people done to work around these issues?
Is there anything that can be done to make CORS more suitable for trusted backends?
Links
Just ran into this issue. Setting the HTTP headers for the 401 response did the trick for me. The library I was using wasn't doing this properly without some customization. e.g.:
self.headers["Access-Control-Max-Age"] = '1728000'
self.headers["Access-Control-Allow-Origin"] = "http://localhost:3001"
self.headers["Access-Control-Allow-Methods"] = "ANY"
self.headers["Access-Control-Allow-Credentials"] = 'true'
I spend a couple of hours today hunting for the answer to this, while developing a web app on Chrome. Some others have written fairly detailed analyses of this situation.
There were two potential issues:
It could be that Chrome was doing a preflight and was getting a non-200 or a non-CORS response in response
For some reason my non-200 (error) status codes were not getting the headers attached. As I found out in this post, this second reason was the issue. Essentially, NGINX only adds headers on successful responses by default. To get my error responses through CORS, it sufficed to change
add_header 'Access-Control-Allow-Origin' '*';
to
add_header 'Access-Control-Allow-Origin' '*' always;
As the article notes, you may also have to add always
to 'Access-Control-Allow-Credentials'
and 'Access-Control-Allow-Headers'
. After doing this, all error codes also went through without CORS issues.
It may be that the program you are using for API serving does the same. Hope this helps and saves someone some time!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With