Enable CORS in IIS 8.5 application

We have a ERP application (Epicor) which provides a REST interface sitting inside of an II 8.5 (win server 2012R2).

No problem doing POST/GET etc using Insomina (a desktop program similar to PostMan)

In IIS we have enabled only Anonymous Authentication.

However, the below request is getting a 401 Error and is blocking because of CORS (even though it has ‘Access-Control-Allow-Origin’:‘*’, )

We are using Chrome V74

     const fetcher = (async () => {
        const url = 'https://epicorapp2.draper.com/ERP10.1Test/api/v1/Erp.BO.JobEntrySvc/GetNextJobNum'
        const raw =  fetch(url, {
          method: 'POST',
          mode: 'cors',
          headers: {
            'Accept': 'application/json',
            'Authorization': 'Basic xxxxxxx=',
            'Access-Control-Allow-Origin':'*',
            'Content-Type': 'application/json'
          },
          body: JSON.stringify({})
        })
        console.log(raw)
        const nJob = await raw.json()
        console.log(nJob)
        return nJob
      })

Here is the error:

    VM1115:1 OPTIONS https://epicorapp2.draper.com/ERP10.1Test/api/v1/Erp.BO.JobEntrySvc/GetNextJobNum 401 (Forbidden)

    dispatchInteractiveEvent @ Main.7485fc72.js:8458
    Main.html:1 Access to fetch at 'https://epicorapp2.draper.com/ERP10.1Test/api/v1/Erp.BO.JobEntrySvc/GetNextJobNum' from origin 'http://localhost' 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. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.

What settings should we be using on IIS to allow the CORS request to go through?

enter image description here

Had that same issue when using an AppServer with SSO Enabled. Deployed a separate instance without SSO and used it for API Calls, worked fine.

Yes @hmwillett and I enabled CORS, and did alot more for about 3hrs :slight_smile: – Turn off SSO, works no problem. We tried Browser Plugins, Header Modification Tools etc… Modfy IIS Poll Settings to Allow OPTIONS etc… Turn off SSO - works no problem.

I bet you already have CORS * in your web.config in - Epicor defaults with * \\SERVERNAME\WhereverYourE10InstanceIs\Server\web.config

2 Likes

Yes. We understand the risk with CORS (we are a very locked down corporate network).

But I am a newbie at IIS admin. Which folder would find that xml file, and where what might it be called.

Also, our IT department (sometimes clueless on this) thinks we might need this to be added to IIS:

We got I.T. to add the cors module, and I updated the the <system.webServer> section of the web.config but I am still getting an CORS error. Can you tell me what is wrong here?

<httpProtocol>
 	<customHeaders>
   		<add name="Access-Control-Allow-Origin" value="*" />
 	</customHeaders>
</httpProtocol>
<cors enabled="true" failUnlistedOrigins="false">
</cors>
  </system.webServer>

This is the error:

Access to fetch at 'https://epicorapp2.draper.com/ERP10.1Test/api/v1/Erp.BO.JobEntrySvc/GetNextJobNum' from origin 'http://ysg4206.draper.com' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: It does not have HTTP ok status.

I don’t know your whole scenario here, but if you are in control of this custom web app that is making the ERP REST Post calls, also make sure you also host the app on draper.com, thus avoiding CORS altogether.
You can’t use wildcard-



when passing credentials.
You can specify one domain though if that works in scenario.
See https://stackoverflow.com/questions/19743396/cors-cannot-use-wildcard-in-access-control-allow-origin-when-credentials-flag-i

I suggested you to add settings in the other section:
Add the following appSettings value: <add key="CorsOrigins" value="*" />

I did not use that module you added, so I don’t know what is wrong in it.

Hello Olga. You are correct, and our I.T. department was clueless. One should not add that Cors Module.

Now I have a slightly different issue. Getting the content of the post GetNextJobNumber().

Here is my fetcher:

const fetcher = (async () => {
    const url = 'https://epicorapp2.draper.com/ERP10.1Test/api/v1/Erp.BO.JobEntrySvc/GetNextJobNum'
    const reply = fetch(url, {
        method: 'POST',
        mode: 'cors',
        headers: {
            'Accept': 'application/json',
            'Authorization': 'Basic eXNnNDIwNjp5ZWNoZXprYWw=',
            'Access-Control-Allow-Origin': '*',
            'Content-Type': 'application/json'
        },
        body: JSON.stringify({})
    })
    let job = await reply
    console.log(job)
})

But the response I get (after waiting for the PROMISE called reply) is:

Response {type: “cors”, url: “https://epicorapp2.draper.com/ERP10.1Test/api/v1/Erp.BO.JobEntrySvc/GetNextJobNum”, redirected: false

and here is what the Chrome debuger says in in the variable job:

  1. body: (…)
  2. bodyUsed: false
  3. headers: Headers {}
  4. ok: true
  5. redirected: false
  6. status: 200
  7. statusText: “OK”
  8. type: “cors”
  9. url: “https://epicorapp2.draper.com/ERP10.1Test/api/v1/Erp.BO.JobEntrySvc/GetNextJobNum
  10. proto: Response

Charlie, I would like to avoid the CORS issue, but we are unable to use the epicor app server as also the IIS content server, and even if we knew what folder on IIS to put web content for the Test (e.g. D:\Epicor\Websites\ERP10.1Test\Server\Web ) find that putting content there is still not visible to the outside.

Beside do not want to use IIS for development, but hot-load our React/Redux forms for development on a sepereate server.

So going from content on ysg.draper.com to epicorapp2.draper.com triggers CORS. Olga’s solution works, and we can tighten the CORS rule to http://*.draper.com or even tighter.

You know, you might just have a bad web.config - Epicor by default ALWAYS sets atleast since 10.2 and I’ve even seen it in my 10.1.500.46

<add key="CorsOrigins" value="*" />

If you did not have that key you might want to take the web.config.template and compare it to your web.config - it is in the same folder.

I have seen users struggle in 10.2 yet somehow they are using a 10.1.400 or 10.1.500 web.config where stuff is diff, and ordered differently, even some versions for OWIN are used are different. because they did not rebuild their AppServer during Major Upgrades, bu merely “Upgraded it” which is a no-no.

Also you must understand your error, you might get “GET/POST/PUT/PATCH” to work, it is the “OPTIONS” that isn’t working. You want to be authenticated to execute “GET/POST/PUT/PATCH” but not necessarily for “OPTIONS”.

You can enable, disable and kill CORS all day long, if the OPTIONS Verb is not allowed you will rarely win.

Your Issue is a generic OPTIONS is Excluded from Honoring any Rule whatsoever. Its not a Client Side Issue, you can grab all the tools in the world to fake headers - your OPTIONS Verb will always say “No Thanks” on the Server Side.

I tried all of the above suggestions as well as others I found on SO and what mattered in my situation was we had Request Filtering enabled on IIS and the OPTIONS HTTP Verb was not in the list of allowed verbs. Once I added it I was able to sort out the rest of it.

In our case it was request filtering in IIS disabling OPTIONS verb at the root web application level. Open up IIS Manager, click on root application, click on Request Filtering, if OPTIONS appears in list either remove or Allow Verb. Wish I had checked this first as lots of wasted time.

It’s always been a culprit of a bad web.config

In our case I always say SSO / WebDAV was the culprit because we couldn’t even open the “Active Homepage”. If you have SSO it wouldnt work, because if you go to get a token to the Token Service it will ask you to login with your Windows Creds first – well how can a web app grab a token if SSO is saying no. =) Perhaps its just us having a misconfig in WebDAV.

Perhaps in Epicor Case its to remove the Handlers for it to avoid Auth for the preflights – inspect you web.config vs web.config.template and if you are using SSO WebDAV you might need to enable more things…

Examples of what it should look like:

Perhaps once again your using an older web.config or need to do

Good Luck.

EDIT:
As a matter of fact our ERP Admin Upgraded our Test Environment, while he Rebuild our DEV Environment look at the difference both 10.2.300.13



Test since its an older web.config exhibits all kinds of API Issues. Token, fix that, then its something else etc…
2019-05-16_1400

While DEV Works fine.

Conclusion

Make sure your web.config is up-to-date by comparing it to a fresh AppServers web.config or web.config.template (which may or may not be up-to-date) or reaching out to a community member to share their web.config for your version. Even better rebuild your AppServer, you can change web.config values and versions but you might not have the proper new versions if you did an Upgrade and not a Rebuild.

Tool to Compare

2 Likes

Well, I followed OLGA’s advice, look out all the httpProtocol sections, and only did the add to

and it works. My issue was the await for the fetch for the javascript. This is method is now working for me (see below).

my only remaining question is that I tried:

To add a little bit of CORS protection, but that does not work. So far only “*” works. Any suggestions?

Here is my working fetcher for next job number.

const fetchJobNum = (async () => {
    const url = 'https://epicorapp2.draper.com/ERP10.1Test/api/v1/Erp.BO.JobEntrySvc/GetNextJobNum'
    const reply = await fetch(url, {
        method: 'POST',
        mode: 'cors',
        headers: {
            'Accept': 'application/json',
            'Authorization': 'Basic <no, I don't show you clearText password',
            'Access-Control-Allow-Origin': '*',
            'Content-Type': 'application/json'
        },
        body: JSON.stringify({})
    })
    let rsp = await reply.json()
    let job = rsp.parameters.opNextJobNum
    return job
})

Just change the CorsOrigins Value, you have to specify http and https explicitly for it to work for both if you need it, populate it with your hostname. You should be able to add more via comma delimitation.

Examples:

<add key="CorsOrigins" value="*" /> - default option, CORS allows access to all sites.
<add key="CorsOrigins" value="https://localhost" /> - CORS only allows requests from only https://localhost .
<add key="CorsOrigins" value="https://localhost, http://localhost" /> - Both https and http

I guess the bottom line is that you cannot use wildcards in that value parameter. (except for ‘*’’).

Thats ok, we will have only a few dev IIS servers for developers to do React with Hot Loading, and then
we only need to do this for the test or pilot databases. The live production system will not need CORS.
We can throw that on the IIS server that is on the same host as the appServer.