How to make XMLHttpRequest cross-domain withCredentials, HTTP Authorization (CORS)?

63,386

Solution 1

I've written an article with a complete CORS setup.

I found several issues that can result in this problem:

  1. The Access-Control-Allow-Origin cannot be a wildcard if credentials are being used. It's easiest just to copy the Origin header of the request to this field. It's entirely unclear why the standard would disallow a wildcard.
  2. Firefox caches the Access-Control results even if you clear the cache (perhaps for the session). Restarting forced it to do a new OPTIONS request. To aid in debugging I added the header Access-Control-Max-Age: 1
  3. The username/password of the open command is apparently not usable as the credentials. You must add an Authorization header yourself. xhr.setRequestHeader( 'Authorization', 'Basic ' + btoa( user + ':' + pass ) )

Overall the withCredentials system is rather braindead. It's easier to simply write a server that accepts the authorization as part of the body of the request.

Solution 2

The only way I found that works with preflight requests is:

  • Disable autentication for OPTIONS method requets by moving autentication logic to code (e.g. from Apache to PHP).
  • Use jQuery ajax call with withCredentials:
$.ajax({
    type: 'POST',
    xhrFields: {
        withCredentials: true
    },
    ...
});

Surprisingly, if I just set xhr.withCredentials to true it doesn't work:

$.ajax({
    type: 'POST',
    beforeSend: function (jqxhr) {
        jqxhr.withCredentials = true; // This doesn't work
    },
    ...
});
Share:
63,386
edA-qa mort-ora-y
Author by

edA-qa mort-ora-y

I'm an experienced programmer who has worked in numerous domains such graphic rendering, telecommunications, scientific applications, business processes, video games, financial platforms, and development products. For more about programming visit Musing Mortoray, or check out my live stream. If you’d like private mentoring check out my profile at Codementor. I’m the creator of the Leaf programming language.

Updated on August 05, 2020

Comments

  • edA-qa mort-ora-y
    edA-qa mort-ora-y almost 4 years

    I'm unable to make a cross-domain request with an Authorization header (testing with Firefox). I have requests working without authentication, but once I set withCredentials to true I am no longer able to read the response from the server.

    On the server I send back these headers (using an after_request method in Flask):

    resp.headers['Access-Control-Allow-Origin'] = '*'
    resp.headers['Access-Control-Allow-Credentials'] = 'true'
    resp.headers['Access-Control-Allow-Methods'] = 'POST, OPTIONS'
    resp.headers['Access-Control-Allow-Headers'] = 'Authorization'
    

    No OPTIONS call is ever actually made by Firefox. On the client I make an XMLHttpRequest call:

    var xhr = new XMLHttpRequest()
    xhr.open( 'POST', 'http://test.local:8002/test/upload', true)
    xhr.withCredentials = true
    xhr.onreadystatechange = function() {
        console.log( xhr.status, xhr.statusText )
    }
    xhr.send(fd)
    

    Without withCredentials set the log statement will log the expecting information to the console. Once I set the value however the xhr doesn't allow access and I just write a 0 value and an empty string. I haven't set the authorization header here, but that shouldn't affect my ability to read the result.

    If I attempt to add a username/password to the "open" command I get a NS_ERROR_DOM_BAD_URI: Access to restricted URI denied error.

    What am I doing wrong?