How configure Azure API Management for CORS
Asked Answered
K

4

11

I have create an Azure API Management Service and connected my APIs. I added CORS policies to them.

enter image description here

I checked the Calculate effective policy and the result is this policy

enter image description here

<policies>
    <inbound>
        <!-- base: Begin Product scope -->
        <!-- base: Begin Global scope -->
        <cors allow-credentials="true">
            <allowed-origins>
                <origin>https://developer.mydomain.com</origin>
            </allowed-origins>
            <allowed-methods preflight-result-max-age="300">
                <method>*</method>
            </allowed-methods>
            <allowed-headers>
                <header>*</header>
            </allowed-headers>
            <expose-headers>
                <header>*</header>
            </expose-headers>
        </cors>
        <!-- base: End Global scope -->
        <!-- base: End Product scope -->
        <cors>
            <allowed-origins>
                <origin>*</origin>
            </allowed-origins>
            <allowed-methods>
                <method>GET</method>
                <method>POST</method>
            </allowed-methods>
        </cors>
    </inbound>
    <backend>
        <!-- base: Begin Product scope -->
        <!-- base: Begin Global scope -->
        <forward-request />
        <!-- base: End Global scope -->
        <!-- base: End Product scope -->
    </backend>
    <outbound />
    <on-error />
</policies>

enter image description here

If I call the API with C#, it is working. Then, I created a simple JavaScript to call the API but there is no way to avoid CORS. I tried different JavaScript

<html>
    <head>
        <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
    </head>
    <body>
        <script>
        var url = '';
        var headerKey = 'subscription-Key';
        var headerValue = 'e1e21';

        $.ajax({
            url: url,
            beforeSend: function(xhrObj){
                // Request headers
                xhrObj.setRequestHeader("Origin","https://www.puresourcecode.com/");
                xhrObj.setRequestHeader("Access-Control-Allow-Origin","https://www.puresourcecode.com/");
                xhrObj.setRequestHeader("Access-Control-Request-Method","GET");
                xhrObj.setRequestHeader("Access-Control-Allow-Credentials","true");
                xhrObj.setRequestHeader("Access-Control-Request-Headers","X-Custom-Header");
                xhrObj.setRequestHeader("Access-Control-Allow-Headers","Origin, Content-Type, Accept, Authorization, X-Request-With");
                xhrObj.setRequestHeader(headerKey,headerValue);
            },
            type: "GET",
            contentType: "application/json; charset=utf-8",
        })
        .done(function(data) {
            alert("success");
        })
        .fail(function() {
            alert("error");
        });

        // jQuery preflight request
        $.ajax({
            type: "GET",
            headers: {headerKey: headerValue},
            url: url
        }).done(function (data) {
            console.log(data);
        });
        
        // XMLHttpRequest preflight request
        var xhr = new XMLHttpRequest();
        xhr.open("GET", url, true);
        xhr.setRequestHeader(headerKey, headerValue);
        xhr.onload = function () {
            console.log(xhr.responseText);
        };
        xhr.send();
        
        // Fetch preflight request
        var myHeaders = new Headers();
        myHeaders.append(headerKey, headerValue);
        fetch(url, {
            headers: myHeaders
        }).then(function (response) {
            return response.json();
        }).then(function (json) {
            console.log(json);
        });
        </script>
    </body>
</html>

All of them are failing because of CORS. I tried to add preflight-result-max-age="300" and also specified the allowed-headers like the one I use to pass the subscription key without success. I also tried to copy this file in my server with the same error.

enter image description here

As origin I set * because I thought in this way every request for every URL is accepted but obviously not.

What is the correct settings to apply in the Azure API Management to avoid CORS?

Knowledgeable answered 24/4, 2021 at 14:50 Comment(2)
What happens when you install the test.html file on a web server and test? (I mean rather than opening the test.html file into your browser directly from your local filesystem.)Jamnes
I receive the same errorKnowledgeable
K
13

I think to issue happens when you enable the Developer portal. Then, in the configuration for the portal, you enable CORS. At this point, Azure automatically adds a global policy to the API Management Service but you don't know that.

<policies>
    <inbound>
        <!-- base: Begin Global scope -->
        <cors allow-credentials="true">
            <allowed-origins>
                <origin>https://developer.mydomain.com</origin>
            </allowed-origins>
            <allowed-methods preflight-result-max-age="300">
                <method>*</method>
            </allowed-methods>
            <allowed-headers>
                <header>*</header>
            </allowed-headers>
            <expose-headers>
                <header>*</header>
            </expose-headers>
        </cors>
        <!-- base: End Global scope -->
    </inbound>
</policies>

Happily, you configure CORS in your API and when you try to call them from JavaScript, you are facing the CORS error.

If you open the CORS configuration in the Azure portal you can see something like that:

<policies>
    <inbound>
        <base />
        <cors>
            <allowed-origins>
                <origin>*</origin>
            </allowed-origins>
            <allowed-methods preflight-result-max-age="300">
                <method>GET</method>
                <method>POST</method>
            </allowed-methods>
            <allowed-headers>
                <header>*</header>
                <header />
            </allowed-headers>
            <expose-headers>
                <header>*</header>
            </expose-headers>
        </cors>
    </inbound>
    <backend>
        <base />
    </backend>
    <outbound>
        <base />
    </outbound>
    <on-error>
        <base />
    </on-error>
</policies>

My understanding is the portal combine the base configuration with yours. So, like in Active Directory the more restricted policy is applying. That means in my example, only the Developer Portal can call the APIs without CORS issue.

Solution

Delete <base /> under <inbound> and save.

Knowledgeable answered 26/4, 2021 at 11:9 Comment(2)
+1, that solved a similar problem I was facing. However contradict my understanding of a base reference, which would inherit all from the father plus apply those specificities.Valerivaleria
@Valerivaleria true - it will run both policies so the answer "the more restricted policy is applying" is only true because global is running first. Looks like the OP did the right thing and resolved the policy scope. We don't apply any CORS policy at the global scope since each API has it's own origins, methods, and headers. Something to look out for: removing the <base /> attribute can be a huge issue if there are important governance policies there.Carpentry
A
9

UI Screenshot

I was running into the same issue on every API Management I set up. I keep forgetting what I did to fix it, so this is also a reminder to my future self.

  • Add a CORS policy,
  • Allowed headers *
  • Enable Allow credentials
  • Include Slash in URL

enter image description here

Amentia answered 10/1, 2023 at 12:1 Comment(1)
The screenshot saved my day. Thank you.Kaylor
H
3

Yes, updating the CORS in the global setting solved our problem. We spent a lot of time troubleshooting by overriding the policy for a specific service to identify the root cause. It turned out to be that we have to apply the CORS policy at the global level.API Management Service -> APIS -> All APIS -> Inbound processing settings

Hyperpituitarism answered 1/10, 2022 at 15:10 Comment(1)
In my case I had to wildcard the headers, which I hadn't considered until I followed your link. Thanks! <allowed-headers><header>*</header></allowed-headers><expose-headers><header>*</header></expose-headers>Yellowish
S
0

I think the issue is not about applying the policy at the child or root level. The issue occurs for me when I apply the policy at multiple overlapping levels. Clearly a glitch in the tooling

Semiaquatic answered 8/4 at 0:37 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.