The value of the 'Access-Control-Allow-Origin' header in the response must not be the wildcard '*'

Hi,
Before I get directed to use the search feature:

  1. I searched and looked through probably all topics related to CORS at this point.
  2. I do understand how CORS works and what it’s supposed to guard against.
  3. My issue is different from the topics found using search, the header is present, but has an invalid value.

I’m trying to use the basic REST API from a JavaScript application. I need to set withCredentials to true so that the Authorization header is sent across different origins. However, Chrome (v. 91), then hides the response from me due to CORS policy, with the following error:
Access to XMLHttpRequest at 'https://api.company-information.service.gov.uk/company/11322864' from origin 'https://<origin>' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: The value of the 'Access-Control-Allow-Origin' header in the response must not be the wildcard '*' when the request's credentials mode is 'include'. The credentials mode of requests initiated by the XMLHttpRequest is controlled by the withCredentials attribute.

As you can see from the error and as I can see from the F12 Network tab, the ‘Access-Control-Allow-Origin’ in the preflight OPTIONS request is a wildcard ‘*’. This makes this issue different from the topics I could find, where that header was missing. My API key has a JavaScript domain value provided that exactly matches the one I’m trying to get the response from and still, the header is a wildcard. For Chrome to allow the application to read the response from the GET request, the OPTIONS request has to specify my origin in the ‘Access-Control-Allow-Origin’ header, not just the wildcard.

Does setting JavaScript domains for a key takes a long time (more than several hours) to start working? Do I need to do anything else to change the wildcard '*' ‘Access-Control-Allow-Origin’ header on OPTIONS request except setting the JavaScript domains entries for my REST API key?

Just for clarity, the authentication I’m using works, I am able to get the requested resource using curl.

Thank you for your time

We think if you make any request other than an OPTIONS request it should work

Hi!
Thank you for the reply!
Unfortunately, I cannot avoid making an OPTIONS request, as that is a CORS preflight request automatically sent by the browser. It is unavoidable when an HTTP request with a header other than the four safe headers is sent by JavaScript, in this case Authorization.

Below is the OPTIONS request generated by the browser before my GET https://api.company-information.service.gov.uk/company/11322864 with Autorization: Basic <encoded API key:> header is sent. Note that I don’t control the headers sent in the OPTIONS request. That’s on purpose, so e.g. rogue JavaScript’s request cannot fake being from a different origin.

OPTIONS /company/11322864 HTTP/1.1
Host: api.company-information.service.gov.uk
Connection: keep-alive
Pragma: no-cache
Cache-Control: no-cache
Accept: */*
Access-Control-Request-Method: GET
Access-Control-Request-Headers: authorization
Origin: https://<app url, matching one of my API key JavaScript domains>
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.164 Safari/537.36
Sec-Fetch-Mode: cors
Sec-Fetch-Site: cross-site
Sec-Fetch-Dest: empty
Referer: <same as Origin>/
Accept-Encoding: gzip, deflate, br
Accept-Language: en-GB,en;q=0.9

Your server’s response is the following:

HTTP/1.1 204 No Content
Date: Fri, 20 Aug 2021 03:48:07 GMT
Content-Type: text/html;charset=UTF-8
Access-Control-Allow-Credentials: true
Access-Control-Allow-Headers: origin,content-type,content-length,user-agent,host,accept,authorization
Access-Control-Allow-Methods: OPTIONS,GET
Access-Control-Allow-Origin: *
Access-Control-Expose-Headers: Location,www-authenticate,cache-control,pragma,content-type,expires,last-modified
Access-Control-Max-Age: 3600
Allow: OPTIONS,GET
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Pragma: no-cache
Server: CompaniesHouse
Connection: Keep-Al

As noted in my first post, Access-Control-Allow-Origin: * causes Chrome to block the response to the GET request from being read with the error I pasted there. There is a possible solution to this — if, instead, you were sending the requester Origin’s header contents back in the OPTIONS Access-Control-Allow-Origin header, that would make it possible for JavaScript to read your responses. For example, Access-Control-Allow-Origin: https://my-app.prd.domain.com, would allow JavaScript hosted at https://my-app.prd.domain.com to read the response to the subsequent GET request.

Thank you for your time spent on this,
Piotr

This seems to be a feature of the way our API code works. I have raised a request for further investigation which will depend on work load priorities.

Cannot be a ‘feature’ if it breaks protocol or if the ‘feature’ makes it impossible to consume the service through a browser, it is a bug.

It seems as though the CORS has disabled for the OPTION call. We are reviewing the reasons for this. It would help if we could see the code for the Javascript call.

Thanks for the quick replies! While I cannot share our codebase, below is a snippet that reproduces the issue just as well, using bare bones JavaScript. Just save this as index.html and run it in your browser. Replacing <API_KEY> with a valid API key isn’t required, the error occurs for any kind of response, not just 2xx.

<!DOCTYPE html>
<html lang="en-GB">
    <head>
        <meta charset="utf-8" />
        <script>
            function onLoad() {
                console.log(this.responseText);
            }
            var request = new XMLHttpRequest();
            request.addEventListener("load", onLoad);
            request.open("GET", "https://api.company-information.service.gov.uk/company/00041424");
            request.setRequestHeader('Authorization',`Basic ${btoa('<API_KEY>:')}`);
            request.withCredentials = true;
            request.send();
        </script>
    </head>
    <body>
        In e.g. Chrome, press F12 to view the error in the console.
    </body>
</html>

Since this sends a request with null origin, a full, proper implementation that would later allow to verify a fix, would require hosting this on a non-localhost HTTP server with a valid API key.

It seems that the expectation is that our API will be called from backend services and not from browser clients. However I have raised this as an issue internally and I will respond again when this has been reviewed.