Home > OS >  Why am I getting a CORS error with jQuery but not with XMLHttpRequest?
Why am I getting a CORS error with jQuery but not with XMLHttpRequest?

Time:12-31

We are facing weird issue while making a Cross domain API call from from UI code base where one way of Ajax request (vanilla JS) works however another doesn't (jQuery). Any pointer will help.

Cross Domain Success call

let url = 'https://somedomain.com/test-api/links';
var xhr = new XMLHttpRequest();
xhr.open("GET", url, true);
xhr.responseType = 'application/json';
xhr.onload = function () {
  // Successful request
  if (xhr.status == 200) {
    console.log('success')
  }
};
xhr.onerror = function () {
  // Crossdomain request denied
  if (xhr.status === 0) {
    console.log(xhr.response)
  }
};

xhr.crossDomain = true;
xhr.withCredentials = true;
xhr.send();

However with following code base (jQuery) we are getting error message stating -:

Access to XMLHttpRequest at 'https://sourcedomain.com/test-api/links' from origin 'https://callerDomain.com' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: The value of the 'Access-Control-Allow-Credentials' header in the response is '' which must be 'true' when the request's credentials mode is 'include'. The credentials mode of requests initiated by the XMLHttpRequest is controlled by the withCredentials attribute

.ajax({
      url: "https://somedomain.com/test-api/links",
      method: 'GET',
      contentType: 'application/json',
      dataType: "json",
      crossDomain : true,
      xhrFields: {
        withCredentials: true
      }
    }).then((resp) => {
      console.log(resp)
    }).catch((err) => {
      console.log(err)
    });

Apache Confirguration on sourcedomain.com

Header always set Access-Control-Allow-Methods "POST, GET, OPTIONS, DELETE, PUT"
Header set Access-Control-Allow-Credentials true
Header always set Access-Control-Allow-Origin "https://callerDomain.com"
Header always set Access-Control-Allow-Headers "*"

Update: Since below combination is invalida i updated apacahe configuration to

 Header always set Access-Control-Allow-Headers "*"
 Header set Access-Control-Allow-Credentials true

Apache updated Confirguration on sourcedomain.com

Header always set Access-Control-Allow-Methods "POST, GET, OPTIONS, DELETE, PUT"
Header set Access-Control-Allow-Credentials true
Header always set Access-Control-Allow-Origin "https://callerDomain.com"
//for testing only
Header set   Access-Control-Allow-Headers       "Accept, Accept-CH, Accept-Charset, Accept-Datetime, Accept-Encoding, Accept-Ext, Accept-Features, Accept-Language, Accept-Params, Accept-Ranges, Access-Control-Allow-Credentials, Access-Control-Allow-Headers, Access-Control-Allow-Methods, Access-Control-Allow-Origin, Access-Control-Expose-Headers, Access-Control-Max-Age, Access-Control-Request-Headers, Access-Control-Request-Method, Age, Allow, Alternates, Authentication-Info, Authorization, C-Ext, C-Man, C-Opt, C-PEP, C-PEP-Info, CONNECT, Cache-Control, Compliance, Connection, Content-Base, Content-Disposition, Content-Encoding, Content-ID, Content-Language, Content-Length, Content-Location, Content-MD5, Content-Range, Content-Script-Type, Content-Security-Policy, Content-Style-Type, Content-Transfer-Encoding, Content-Type, Content-Version, Cookie, Cost, DAV, DELETE, DNT, DPR, Date, Default-Style, Delta-Base, Depth, Derived-From, Destination, Differential-ID, Digest, ETag, Expect, Expires, Ext, From, GET, GetProfile, HEAD, HTTP-date, Host, IM, If, If-Match, If-Modified-Since, If-None-Match, If-Range, If-Unmodified-Since, Keep-Alive, Label, Last-Event-ID, Last-Modified, Link, Location, Lock-Token, MIME-Version, Man, Max-Forwards, Media-Range, Message-ID, Meter, Negotiate, Non-Compliance, OPTION, OPTIONS, OWS, Opt, Optional, Ordering-Type, Origin, Overwrite, P3P, PEP, PICS-Label, POST, PUT, Pep-Info, Permanent, Position, Pragma, ProfileObject, Protocol, Protocol-Query, Protocol-Request, Proxy-Authenticate, Proxy-Authentication-Info, Proxy-Authorization, Proxy-Features, Proxy-Instruction, Public, RWS, Range, Referer, Refresh, Resolution-Hint, Resolver-Location, Retry-After, Safe, Sec-Websocket-Extensions, Sec-Websocket-Key, Sec-Websocket-Origin, Sec-Websocket-Protocol, Sec-Websocket-Version, Security-Scheme, Server, Set-Cookie, Set-Cookie2, SetProfile, SoapAction, Status, Status-URI, Strict-Transport-Security, SubOK, Subst, Surrogate-Capability, Surrogate-Control, TCN, TE, TRACE, Timeout, Title, Trailer, Transfer-Encoding, UA-Color, UA-Media, UA-Pixels, UA-Resolution, UA-Windowpixels, URI, Upgrade, User-Agent, Variant-Vary, Vary, Version, Via, Viewport-Width, WWW-Authenticate, Want-Digest, Warning, Width, X-Content-Duration, X-Content-Security-Policy, X-Content-Type-Options, X-CustomHeader, X-DNSPrefetch-Control, X-Forwarded-For, X-Forwarded-Port, X-Forwarded-Proto, X-Frame-Options, X-Modified, X-OTHER, X-PING, X-PINGOTHER, X-Powered-By, X-Requested-With"

Response to the preflight request enter image description here

enter image description here

CodePudding user response:

Problem(s)

The two requests are not equivalent

For the one sent via XHR, you're only setting the content type of the response:

xhr.open("GET", url, true);
// ...
xhr.responseType = 'application/json';

However, for the request sent via jQuery, you're setting the content type of the request itself to application/json:

.ajax({
  // ...
  method: 'GET',
  contentType: 'application/json',
  // ...
});

Use of the wildcard in conjunction with a credentialed request

A value of application/json for a request's content type makes it non-simple; as a result, a preflight request is triggered by your browser. However, you're responding to this preflight request with

Access-Control-Allow-Headers: *
Access-Control-Allow-Credentials: true

which is an incompatible combination. Therefore, CORS preflight fails, as explained in your browser's error message.

Invariably responding to preflight requests with a 403 status

Your latest edit shows that the status of the response to the preflight request is 403. However, for CORS preflight to be successful, the status must be in the 2xx range; see this other recent answer of mine about that.

Solution(s)

  1. Either don't specify the request's content-type to keep it simple, or (preferably) explicitly allow the Content-Type header in the Access-Control-Allow-Headers header rather than using the wildcard.
  2. Make sure you respond with a 2xx status to successful preflight requests. Be mindful that, because preflight requests never carry credentials, you should handle such requests in front of any authorisation middleware.
  • Related