3

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

lalatnayak
  • 160
  • 1
  • 6
  • 21
  • Please go to this link https://stackoverflow.com/questions/23245636/can-not-get-put-to-work-in-asp-net-webapi Read my solution. – Gyana Jul 11 '22 at 11:29

4 Answers4

1

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..

Matrim
  • 645
  • 7
  • 13
0

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>
Searching
  • 2,269
  • 1
  • 17
  • 28
  • The header clearly is not being added in the response. What could be causing it? – lalatnayak Oct 14 '16 at 15:06
  • So I take that all the other parts of the headers is correctly being set as you've got in the policy. Right? I've updated the answer to show where you can try adding the header mentioned. Are you able to show what OPTIONS method returns,the pre-flight request, screenshot ? [Enable Cors] (https://www.asp.net/web-api/overview/security/enabling-cross-origin-requests-in-web-api) if you haven't tried yet. – Searching Oct 14 '16 at 19:17
  • Config works, in fact that's how it was. But I need to switch to code as I need t add multiple origins to the allowed list. – lalatnayak Oct 14 '16 at 19:22
  • Hmmmm... try commenting the header addition part completely and add `_policy.AllowAnyHeader = true;` just to make sure the headers you add is not contradicting the origins you add.. – Searching Oct 14 '16 at 20:13
  • All right so I used HttpClient to make a request to my API (After Enabling CORS via code). I see that the response has the appropriate header. Strange that the browser is unable to see the header. Any thoughts? – lalatnayak Oct 21 '16 at 19:46
  • I was going to suggest using fiddler or similar tool.. i dont think these clients send an `OPTIONS` request that chrome does. it directly performs GET. Try sending `OPTIONS` via Fiddler. See what happens. – Searching Oct 21 '16 at 19:53
  • Tried sending an options request and the response does not have the header. – lalatnayak Oct 21 '16 at 20:09
  • Do you see the `Access-Control-Allow-Origin` in the `GET` atleast with the right origins ? Are you able to share the ajax request you are sending ? I just want to see how/what you are setting. – Searching Oct 21 '16 at 20:21
  • No I don't see in the headers in the GET response as well. I'm trying from PostMan. – lalatnayak Oct 21 '16 at 20:38
  • I added screenshots in the question – lalatnayak Oct 21 '16 at 20:48
  • Does the controller have `[TCCorsPolicyProvider]` decorated ? This seems odd. But I'm sure given numerous questions and answers around this topic, there seems to be no single solution. I suggest you try with a clean slate by allowing everything `*`, a very basic config and then start restricting. Also check if you are able to add datatype as `jsonp` for your ajax to ignore the pre-flight request for `OPTIONS`. You may also hack the OPTIONS request in `OnActionExecuted` and append your required headers. I might start another answer, before this comments get any bigger.:) – Searching Oct 21 '16 at 20:49
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/126365/discussion-between-lalatnayak-and-searching). – lalatnayak Oct 21 '16 at 20:53
  • @lalatnayak did you get it to work? I'm having the same issue. – strider Feb 05 '19 at 16:15
0

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! :)

Kristian Jay
  • 121
  • 9
0

In .net 6 all you need to do is make changes in your Program.cs file by adding each origin in a string separated by comma.

var  MyCorsPolicy = "_myCorsPolicy";

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddCors(options =>
{ 
    options.AddPolicy(name: MyCorsPolicy,
        policy  =>  
        {
            policy.WithOrigins("http://localhost:4200",
               "http://www.contoso.com");
        }
    );
});
builder.Services.AddControllers();
var app = builder.Build();
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseCors(MyCorsPolicy);
app.UseAuthorization();
app.MapControllers();
app.Run();
Pepe Alvarez
  • 1,218
  • 1
  • 9
  • 15