2017-04-08

Adding CORS to ASP MVC WebApi to support to Angular-Cli

Imagine going to a website, say a forum of some kind, and viewing a page that a malicious user has posted information to and that post contains some JavaScript that reads your cookie and sends that information to another server, allowing the malicious user to steal your authentication cookie and then use that website as you.

Well written websites will not allow this, but not all websites are well written. So, to err on the safe side Web Browsers will only allow JavaScript to call out to the same domain that served the webpage the browser has received. So, it wouldn't be possible to retrieve a page from www.MyFavouriteForums.com and have some malicious JavaScript in a page you are viewing steal your cookie and send it to www.MyMaliciousCookieStealer.com

This is good practice, but sometimes you need the webpage you serve to be able to call out to other domains. A good example is when you are writing an Angular JS website. Executing "npm start" will run a developer webserver and let you access the app you are developing via the URL http://localhost:4200 - If the WebApi server you are accessing is on http://localhost:12345 this is not the same origin. The domain is the same (localhost), but the port is different, so they are not equal.

When you try to call out and grab some data you might see the following error message.

No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:4200' is therefore not allowed access.

To solve this you must edit your Web.Config file. Inside the system.webServer section add

    <httpProtocol>
      <customHeaders>
        <add name="Access-Control-Allow-Origin" value="*" />
        <add name="Access-Control-Allow-Headers" value="Content-Type" />
        <add name="Access-Control-Allow-Methods" value="GET, POST, PUT, DELETE, OPTIONS" />      
      </customHeaders>
    </httpProtocol>
In the above example I have set Access-Control-Allow-Origin to *. You can set this to a specific value if you wish, but for development purposes I have instructed the server to accept requests from any origin. This will tell the web server to add the headers to any responses, which will tell the Browser that the origin of the source page you are viewing is permitted to access it.

When you deploy you will probably serve your Angular app from the same origin that runs the WebApi app. If that is the case then you will need to remove those headers from Web.Config when publishing your website.

In Web.Release.Config

  <system.webServer>
    <httpProtocol>
      <customHeaders>
        <add name="Access-Control-Allow-Origin" xdt:Transform="Remove" />
        <add name="Access-Control-Allow-Headers" xdt:Transform="Remove" />
        <add name="Access-Control-Allow-Methods" xdt:Transform="Remove" />
      </customHeaders>
    </httpProtocol>
  </system.webServer>
If the Angular JS app is served from a different origin then put the real origin in the Web.Config rather than *.

On my machine the express server that runs when you type "npm start" returns a header that looks like this

Access-Control-Allow-Origin:*
This tells the Browser that the page served should allow JavaScript to call out to any other server, and is only present because we are running a development server. If your server is not returning this header then your page will not be permitted to call out to any origin other than the one that served it.  In that case you will need to edit the server JavaScript to add the header.

Edit node_modules\webpack-dev-server\lib\Server.js

Search for "var app" which should have "new express" in the same line.  Add the following beneath that line
    app.use(function (req, res, next) {
        res.header("Access-Control-Allow-Origin", "*");
        res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
        next();
    });


No comments: