javascriptcorsxmlhttprequestnode-requestrequest-promise

HTTP Auth request fails in browser due to CORS, ok in Node


I'm stuck trying to diagnose a problem where a GET request with HTTP Basic Auth succeeds in Node, but fails in the browser. The problem manifests directly as a CORS failure (there's no Access-Control-Allow-Origin on the 401 page).

request.js:119 OPTIONS http://HOST?PARAMS 401 (Unauthorized)
ClientRequest._onFinish @ request.js:119
(anonymous) @ request.js:61
...

17:53:59.170 localhost/:1 Failed to load http://HOST?PARAMS: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:9966' is therefore not allowed access. The response had HTTP status code 401. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.

The request looks like this:

const request = require("request-promise");
const options = {"url":"http://...?","auth":{"user":"...","pass":"..."},"json":true,"qs":{...},"headers":{},"resolveWithFullResponse":true}
request.get(options).then(...);

How this request shows up in Chrome:

enter image description here

What's surprising here to me:

What I would like to know:

Running what would be the equivalent request on the command line seems to work fine:

curl -I 'http://MYUSERNAME:MYPASSWORD@SAMEURL?SAME=PARAMETERS' -H 'Origin: http://localhost:4466' -X OPTIONS
HTTP/1.1 200 OK
Date: Wed, 20 Jun 2018 07:29:28 GMT
Server: Apache-Coyote/1.1
Access-Control-Allow-Origin: http://localhost:4466
Access-Control-Allow-Credentials: true
Access-Control-Expose-Headers: Access-Control-Allow-Origin,Access-Control-Allow-Credentials
Vary: Origin
X-Frame-Options: SAMEORIGIN
Allow: GET,HEAD,POST,OPTIONS
Content-Length: 0

However I notice that without the credentials supplied in the URL, there are no CORS headers on the response:

HTTP/1.1 401 Unauthorized
Date: Wed, 20 Jun 2018 07:45:26 GMT
Server: Apache/2.4.29 (Unix) OpenSSL/1.0.2l
WWW-Authenticate: Basic realm="Authentication Required"
Content-Length: 381
Content-Type: text/html; charset=iso-8859-1

Is that the correct behaviour?

Hypothesis

I suspect:

What I'm not sure about:


Solution

  • The server is misconfigured. It’s not properly CORS-enabled. To be properly CORS-enabled it must respond to unauthenticated OPTIONS requests with a 200 or 204 status code.

    Request isn’t “failing” to pass credentials in the OPTIONS preflight request. Request isn’t even making the the OPTIONS request. Instead the browser engine is, on its own.

    And specifying the credentials flag for the request in your own code doesn’t cause credentials to get added to the preflight OPTIONS request. Instead, credentials only get added to the GET request from your own code — that is, if the preflight has succeeded and the browser actually ever moves on to making that GET request.

    That’s because the CORS-protocol requirements in the Fetch spec state that browsers must omit all credentials from preflight OPTIONS requests.

    See https://fetch.spec.whatwg.org/#ref-for-credentials%E2%91%A5:

    a CORS-preflight request never includes credentials

    So the behavior you’ve encountered is intentional, by design.

    For a related explanation in more detail, see the answer at HTTP status code 401 even though I’m sending credentials in the request.