1

We've added a REST API in our Azure API management. However, when trying to access it from our frontend (Vue.js using axios), the requests fail and we get an error saying "Access to XMLHttpRequest at 'https://test-vue-backend.io/' from origin 'https://test-vue-frontend.io' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource." on both GET requests and POST requests.

For the POST request, I can see a Preflight request being made. This request gets a 200 Status code as a response, but the response body is empty. network

The error message for the preflight request is "Access to XMLHttpRequest at 'https://test-vue-backend.io/' from origin 'https://test-vue-frontend.io' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource."

I don't see a preflight request for the GET request. It just fails immediately with the firstly mentioned error message.

This is what the policy looks like on the top level of that API (all operations):

<policies>
    <inbound>
        <base />
        <cors>
            <allowed-origins>
                <origin>https://test-vue-frontend.io</origin>
            </allowed-origins>
            <allowed-methods preflight-result-max-age="300">
                <method>GET</method>
                <method>POST</method>
                <method>PATCH</method>
                <method>DELETE</method>
                <method>OPTIONS</method>
            </allowed-methods>
            <allowed-headers>
                <header>*</header>
            </allowed-headers>
        </cors>
        <choose>
            <when condition="@(context.Request.OriginalUrl.Host.ToLower() == "test-vue-backend.io")">
                <set-backend-service base-url="http://localhost:8080/backend/webapi" />
            </when>
            <when condition="@(context.Request.OriginalUrl.Host.ToLower() == "dev-vue-backend.io")">
                <set-backend-service base-url="http://localhost:8081/backend/webapi" />
            </when>
        </choose>
    </inbound>
    <backend>
        <base />
    </backend>
    <outbound>
        <base />
        <set-header name="Access-Control-Allow-Origin" exists-action="override">
            <value>@(context.Request.Headers.GetValueOrDefault("Origin",""))</value>
        </set-header>
        <set-header name="Access-Control-Allow-Credentials" exists-action="override">
            <value>true</value>
        </set-header>
        <set-header name="Access-Control-Allow-Methods" exists-action="override">
            <value>GET, POST, PATCH, DELETE, OPTIONS</value>
        </set-header>
    </outbound>
    <on-error>
        <base />
    </on-error>
</policies>

All API names have been altered for privacy reasons, but I can assure you they are correct in my configuration and are not the cause of the issue.

The axios POST and GET requests look like this:

axios
  .get("https://test-vue-backend.io/getroute?lang=" + lang)
  .then(response => {
    commit("SET_ADVICEDATA", response.data);
  });

axios
  .post("https://test-vue-backend.io/postuser", null, {
    headers: { Authorization: "Bearer " + token }
  })
  .then(response => {
    commit("SET_USERINFO", response.data);
  });

If I type https://test-vue-backend.io/getroute?lang=nl directly into the browser, it works.

I have checked both REST API service when called from azure APIM returning empty response body with Status code 200 and Azure API Management returning 200 status without response body but both of these seem unrelated to my issue as the forward-request policy is enabled on my top level (all apis) and as I don't have a policy.

Edit: Thanks to the comments below I have found that if I remove the </base> from my inbound and outbound sections, the CORS works. I don't, however, understand what the cause is in the base.

The clause for ALL APIs looks like this:

<policies>
    <inbound>
        <cors allow-credentials="true">
            <allowed-origins>
                <origin>https://other-host-than-the-one-i-need.net</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>
    </inbound>
    <backend>
        <forward-request />
    </backend>
    <outbound />
    <on-error />
</policies>

My best guess would be that it was only allowing the base origin even though the </base> tag was mentioned before my origin, but I am not certain.

sideshowbarker
  • 81,827
  • 26
  • 193
  • 197
Axelle
  • 442
  • 1
  • 9
  • 24
  • Run "Calculate effective policy" for single operation with the product that you are using and paste it here. What does it show? Are there any policies executed before cors are applied? – SteppingRazor Jul 12 '21 at 20:44
  • The fact that the response body for the preflight OPTIONS request is empty, that’s expected. And it causes no problem. But what actually does cause a problem is the fact that the response lacks the Access-Control-Allow-Origin response header and the Access-Control-Allow-Methods response header and the Access-Control-Allow-Headers response header. – sideshowbarker Jul 12 '21 at 22:49
  • @SteppingRazor from what I can see at this point, the API is not listed under any product. Is this an issue? How can I check if any policies are executed before cors are applied? – Axelle Jul 13 '21 at 09:24
  • @sideshowbarker I see. I don't understand however why there are no response headers, as I have configured them in the outbound section. – Axelle Jul 13 '21 at 09:27
  • 1
    I can only speculate that while the configuration in the outbound section may be causing response headers to get added to POST and GET responses, it’s not causing response headers to get added to OPTIONS responses. Server systems typically have special handling for OPTIONS requests and responses that’s separate from the code for handling GET and POST. So I would guess that the preflight OPTIONS request is getting trapped and handled by some other code and so never ends up reaching the code cited in the question. So you would probably want to try to do some tracing of that server code to see – sideshowbarker Jul 13 '21 at 14:25
  • This is likely due to CORS problems. I see you've whitelisted a single origin, does it exactly match web page address from where call is made? – Vitaliy Kurokhtin Jul 13 '21 at 16:35
  • @Axelle when you open policy code for operation, on the right bottom side there is a button called “Calculate effective policy”. Click on it and it will show you all policies in order how they are executed. If there are any policies executed before Cors policy this might be the problem. If API does not have a product that is not a problem. – SteppingRazor Jul 13 '21 at 17:32
  • @VitaliyKurokhtin it does match it exactly, I've always checked the "Host" field on my requests to make sure it's and exact match and it always is. But I'm also pretty certain it's due to the CORS. – Axelle Jul 14 '21 at 09:55
  • @SteppingRazor Thank you, I have found the button. It only lets me calculate the effective policy for a product however as I have to select one. Thanks to some of the logic all of you have suggested here I have found out that if I remove the from both my inbound and outbound the CORS works! I'm trying to find the exact cause though, as my base seems to be less restrictive currently (allow all inbound and no outbound clause). – Axelle Jul 14 '21 at 09:58
  • @sideshowbarker I have since made an edit after which the CORS works, preflight too. I however fail to understand why including the was causing it to fail. – Axelle Jul 14 '21 at 10:04
  • @Axelle, please, check value of Origin (not Host) header. It should match exactly with value specified in CORS policy. – Vitaliy Kurokhtin Jul 14 '21 at 20:30
  • @VitaliyKurokhtin it's an exact match. But as I have mentioned above, it works when removing the tags in both inbound and outbound. I have edited my question to contain the policy for all operations so if you have any idea why this policy was causing my policy on a deeper level to fail that would be great as I have no idea. – Axelle Jul 15 '21 at 11:08

1 Answers1

1

Your policy explains it. Only single CORS policy will be invoked in request life time - the first one met during processing. Thus it's the policy at global level that produces empty 200 response to your OPTIONS request because origin does not match. You have two options:

  1. Make sure there is only single policy in each request pipeline
  2. Use terminate-unmatched-request attribute on CORS policy to control what should be done with request if it doesn't match any of the specified origins.
Vitaliy Kurokhtin
  • 7,205
  • 1
  • 19
  • 18