Why is this CORS request failing only in Firefox?

2019-01-24 05:39发布

I'm implementing CORS with credentials and a preflight request and I'm a bit mystified why the preflight request consistently fails in Firefox 30 but works in Safari (7.0.2) and Chrome 35. I think this issue is different from "Why does the preflight OPTIONS request of an authenticated CORS request work in Chrome but not Firefox?" because I am not getting a 401, but rather a CORS-specific message from the browser client:

"Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at http://myurl.dev.com. This can be fixed by moving the resource to the same domain or enabling CORS."

Without showing source code, here's what I'm doing:

On the server:

Headers for OPTIONS response:

  • Access-Control-Allow-Origin: [[copy origin from the request here]]
  • Access-Control-Allow-Methods: "POST GET OPTIONS"
  • Access-Control-Allow-Headers: "X-Requested-With"
  • Access-Control-Allow-Credentials: "true"

Headers for POST response:

  • Access-Control-Allow-Origin: [[copy origin from the request here]]
  • Access-Control-Allow-Credentials: "true"

In the browser client:

jQuery.ajax({
  url: requestUrl,
  type: 'POST',
  data: getData(),
  xhrFields: {
    withCredentials: true
  }
});

Per the spec, this will trigger a OPTIONS preflight request which needs to have the CORS headers in its response. I've read through the W3C spec several times and I can't identify what I'm doing wrong, if anything, in that preflight response.

2条回答
Explosion°爆炸
2楼-- · 2019-01-24 05:41

I have noticed that when you a send CORS(Cross Origin Resource Sharing) request with cookies set, Firefox doesn't send the required response headers.

Solution:

Below solution adds headers only for OPTIONS requests and accepts requests only from example.com. You can change the implementation for other request methods and expected hosts.

JS CODE

var xmlhttp = new XMLHttpRequest();
xmlhttp.withCredentials = true;

xmlhttp.onreadystatechange = function () {
    if (xmlhttp.readyState == XMLHttpRequest.DONE) {
        if (xmlhttp.status == 200) {
            success_callback(xmlhttp.responseText);
        } else {
            error_callback(xmlhttp.statusText);
        }
    }
};
xmlhttp.open("DELETE", url);
xmlhttp.send(null);

When you send a DELETE request, the browser send a pre-flight request for OPTIONS, which expects Access-Control-Allow-Methods in the response headers. Based on this header value the actual DELETE request is sent. In Firefox, when you send a DELETE request the pre-flight request's response headers do not have expected headers, therefore it fails to send the actual DELETE request.

To overcome this problem use the below NGINX server config.

NGINX CODE

#handle CORS requests by adding required headers
if ($http_origin ~* .example.com) {
    set $cors "CORS-${request_method}";
}

if ($cors = "CORS-OPTIONS") {
    add_header 'Access-Control-Allow-Credentials' 'true';
    add_header 'Access-Control-Allow-Headers' 'Content-Type';
    add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, DELETE';
    add_header 'Access-Control-Allow-Origin' $http_origin;
}

Good read on CORS: https://www.html5rocks.com/en/tutorials/cors/

查看更多
一纸荒年 Trace。
3楼-- · 2019-01-24 05:45

Note that Firefox is the only browser that is compliant here. If parsing of Access-Control-Allow-Methods fails per https://fetch.spec.whatwg.org/#cors-preflight-fetch a network error needs to be returned. And per the ABNF for the header value it is most definitely a comma-separated value.

查看更多
登录 后发表回答