2
votes

I have been trying to enable CORS for multiple origins for my API without any success.

Here is what I have done.

Created a CORS Policy

 [AttributeUsage(AttributeTargets.All, AllowMultiple =false, Inherited =true)]
    public class TCCorsPolicyProvider : Attribute, ICorsPolicyProvider
    {
        private CorsPolicy _policy;

        public TCCorsPolicyProvider()
        {
            _policy = new CorsPolicy
            {
                SupportsCredentials = true
            };
            string[] allowedOrigins = "john.doe,ava.wise".Split(',');
            string[] allowedMethods = "GET,POST,PUT,OPTIONS".Split(',');
            string[] allowedHeaders = "Content-Type,Origin,Authorization,Accept".Split(',');
            // Add allowed origins.
            foreach (string origin in allowedOrigins)
                _policy.Origins.Add(origin);
            foreach (string method in allowedMethods)
                _policy.Methods.Add(method);
            foreach (string header in allowedHeaders)
                _policy.Headers.Add(header);
        }

        public Task<CorsPolicy> GetCorsPolicyAsync(HttpRequestMessage request, CancellationToken cancellationToken)
        {
            return Task.FromResult(_policy);
        }
    }

Created a Factory

public class TCCorsPolicyProviderFactory : ICorsPolicyProviderFactory
    {
        ICorsPolicyProvider _provider;
        public TCCorsPolicyProviderFactory()
        {
            _provider = new TCCorsPolicyProvider();
        }
        public ICorsPolicyProvider GetCorsPolicyProvider(HttpRequestMessage request)
        {
            return _provider;
        }
    }

In WebApiConfig.cs class enabled Cors

config.SetCorsPolicyProviderFactory(new TCCorsPolicyProviderFactory());
            config.EnableCors();

Made sure the appropriate registration is made in Global.asax Application_Start

 GlobalConfiguration.Configure(WebApiConfig.Register);

When the above did not work I even manually applied the Policy Attribute to the my base controller from which all other controllers inherit

[TCCorsPolicyProvider]
    public class BaseApiController : ApiController
    {
        public string IpAddress
        {
            get { return ContextHelper.GetIpAddress(); }
        }

        private bool _disposed;
        protected virtual void Dispose(bool disposing, Action disposeAction)
        {
            if (!_disposed)
            {
                if (disposing)
                {
                    disposeAction();
                }
            }
            _disposed = true;
        }
    }

But I get the following error (Invoking Api from Angular)

XMLHttpRequest cannot load hqidwtcdwa01/api/localizations/reloadCache. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'ava.wise' is therefore not allowed access. The response had HTTP status code 401.

hqidwtcdwa01 is the destination and ava.wise is the origin.

What I have found so far is that the http response headers in the xmlhttp response do not contain Access-Control-Allow-Origin. However when I use HttpCient, I can see the header. HttpClient Response

Postman Response

3

3 Answers

1
votes

I beleive cors theoretically permits multiple origins but actually does not. For my site I create a new CORS Attribute and return a single permitted origin dependant on the incoming, this is much safer than using *

 [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method,
            AllowMultiple = false)]
public class GlobalEnableCorsAttribute :


 Attribute, ICorsPolicyProvider
    {
        public Boolean SupportsCredentials = true;
        public CorsHandler handle = new CorsHandler();
        public async Task<CorsPolicy> GetCorsPolicyAsync(
          HttpRequestMessage request, CancellationToken cancellationToken)
        {
             var corsRequestContext = request.GetCorsRequestContext();
            var originRequested = corsRequestContext.Origin;


            string approvedOrigin = handle.approveCorsOrigin(originRequested);

            if(!string.IsNullOrEmpty(approvedOrigin))
            {
                // Grant CORS request
                var policy = new CorsPolicy
                {
                    AllowAnyHeader = true,
                    AllowAnyMethod = true,
                    SupportsCredentials = true
                };

                // add headers
                policy.Headers.Add("content-type");
                policy.Headers.Add("withcredentials");
                policy.Headers.Add("Access-Control-Allow-Headers");
                policy.Headers.Add("Access-Control-Allow-Origin");
                policy.Headers.Add("Origin");
                policy.Headers.Add("Accept");
                policy.Headers.Add("X-Requested-With");
                policy.Headers.Add("Access-Control-Request-Method");
                policy.Headers.Add("Access - Control - Request - Headers");


                policy.Origins.Add(approvedOrigin);
                return policy;
            }
            else
            {
                // Reject CORS request
                return null;
            }
        }



    }

the origin search takes the permitted origins from a server config value

 public class CorsHandler
{
    public string approveCorsOrigin(string providedOrigin)
    {
        // load list of web.config origins
        string fullList = Properties.Settings.Default.CORSOriginPermittedSite;

        if (!string.IsNullOrEmpty(fullList))
        {
            string[] originArray = fullList.Split(new char[]{ ','}, StringSplitOptions.RemoveEmptyEntries);

            foreach(string approvedOrigin in originArray)
            {
                if (providedOrigin.Trim() == approvedOrigin.Trim())
                {
                    return providedOrigin;
                }
            }
        }
        return null;
    }
}

Usage is as follows

 public static void Register(HttpConfiguration config)
    {
        if (Properties.Settings.Default.CORSOriginPermittedSite != null && !string.IsNullOrWhiteSpace(Properties.Settings.Default.CORSOriginPermittedSite))
        {
            var cors = new GlobalEnableCorsAttribute();
            config.EnableCors(cors);
        }
    }

Finally this code was against a webapi setup but should be comparable and you also (if using similar) need to make the pre-flight cope with the same origin search..

0
votes

I had very similar issue building mvc api with owincontext, I had to add

#if DEBUG //Notice this is only when I'm in debug mode.
 context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { "*" });
#endif

But when I deployed added headers specific to required origin. And since your error is similar, it might help adding Access-Control-Allow-Origin : ava.wise header and check if it resolves. Hope it helps narrow down your issue.

Sample of what you want to have in your headers, Notice the Options method fetches accepted origins to any "*".

enter image description here

---UPDATE---

Please try updating web.config add the header . Custom Headers This should reflect in the response. Obviously this is possible in code but start simple.

 <configuration>   <system.webServer>
  <httpProtocol>
     <customHeaders>
        <remove name="Access-Control-Allow-Origin" />
        <add name="Access-Control-Allow-Origin" value="*" />
     </customHeaders>
  </httpProtocol>   
 </system.webServer>
</configuration>
0
votes

I did it in a dirty hard way, maybe not that suggested but this got me going(assuming you got cors nuget installed, web api 2.0, and .NET 4.5):

web.config:

<appSettings>
    <add key="AllowedDomains" value="http://domain0:8009, http://domain1:8009"/>
 </appSettings>

Configs.cs:

public static string AllowedDomains {
        get {
            return @ConfigurationManager.AppSettings["AllowedDomains"].ToString();
        }
    }

WebApiConfig:

    public static void Register(HttpConfiguration config)
    {
        var attribute = new System.Web.Http.Cors.EnableCorsAttribute(Configs.AllowedDomains, "*", "*"); //domains, headers, methods - you could do the same for the other args.
        config.EnableCors(attribute); //global
    }

To filter by Controller/action, just add the attribute at top:

[System.Web.Http.Cors.EnableCors(origins: "http://domain2:8009", headers: "*", methods: "*")]

Let me know of any progress or comments. Hope this helps. Thanks! :)