1

Closed

Possible Issue - Conflict between Cors in Web API & Microsoft.Owin.Cors?

description

Just wanted to make everyone aware of a potential issue with a possible conflict between the CORS functionality in Web Api V2 & Microsoft.Owin.Cors (v 2.1 Pre-Release). I needed CORS functionality in Web Api Service so i turned CORS functionality on for Web Api:
public static class WebApiConfig
    {
        public static void Register(HttpConfiguration config)
        {
           var cors = new EnableCorsAttribute("*", "*", "*");
            config.EnableCors(cors);
I also needed to utilize the token service for security in web api v2 and since its not a controller the above code doesn't work. So ended up having to use Microsoft.Owin.Cors.
 public void ConfigureAuth(IAppBuilder app)
        {
             app.UseCors(CorsOptions.AllowAll);
........
The problem this caused was two "Access-Control-Allow-Origin" headers to be added to the response when going through the controllers:
HTTP/1.1 200 OK
Cache-Control: no-cache
Pragma: no-cache
Content-Type: application/json; charset=utf-8
Expires: -1
Server: Microsoft-IIS/8.0
Access-Control-Allow-Origin: http://localhost:14880 <--added by Microsoft.Owin.Cors
Access-Control-Allow-Credentials: true
Access-Control-Allow-Origin: * <-- added by Web Api Cors Functionality.
IE11 was fine with this but latest Version of Chrome/Firefox were not. Here is the error from Chrome:
XMLHttpRequest cannot load http://localhost:1987/api/account/register. The 'Access-Control-Allow-Origin' header contains the invalid value 'http://localhost:14880, *'. Origin 'http://localhost:14880' is therefore not allowed access. 
This had me questioning why they didn't use the same CORS mechanism but what if they needed different access security: Only Gets for the Token Service & Everything for the controllers. Should the token functionality be implemented in a controller? Should both of them be checking if there already exists an "Access-Control-Allow-Orgin"?
Closed Dec 17, 2013 at 7:13 PM by nowakra
Combining two cors providers in this way is not valid, the workaround is to make sure only one cors provider applies to any given request.

comments

yishaigalatzer wrote Dec 13, 2013 at 6:30 PM

Basically looks like you are applying CORS twice. So the result is expected (though not desirable).

Are you trying to apply the CORS middleware to the Web API service as well, or to something else?

if you are applying to web API, what is it that you apply it to (the thing you said is not a controller I guess?, can you expand a bit on that). Potential direction here would be to apply the Cors middleware to a token server api service, that is separate from the one you are referring to?

Ncage1974 wrote Dec 13, 2013 at 7:55 PM

The cors functionality baked in web api will only work if your sitting behind a controller. Unfortunately the token functionality in web api isn't behind a controller its in an owin component. So essentially you use the core cors functionality baked in web api for "most" things. Unfortunately you then you have to use Microsoft.Owin.Cors for the token service. While i agree with you that you could implement a "second" token service you really shouldn't "have" to. This adds complexity that really shouldn't have to be added. A service should be able to take care of its own security. I don't want to go down the path of trying to use an external STS service :).

Please be aware this is according to how i understand things currently. I could be incorrect. So don't be shy to correct me if i mispoken.

yishaigalatzer wrote Dec 13, 2013 at 10:29 PM

Yes it does seem broken. We are going to take a look.

Here are a few interesting alternatives:
  1. Fork the pipeline
     // For more information on how to configure your application, visit http://go.microsoft.com/fwlink/?LinkID=316888
        var builder = app.New();
        var config = new HttpConfiguration();
        builder.UseWebApi(config);
    
        var appFunc = (AppFunc)builder.Build(typeof(AppFunc));
    
        app.Map("/token", subApp =>
        {
            subApp.UseCors(CorsOptions.AllowAll);
            subApp.Use(new Func<AppFunc, AppFunc>(next => appFunc));
        });
    
        app.Use(new Func<AppFunc, AppFunc>(next => appFunc));
  2. Use MapWhen, and pass in a predicate, now you can look at the request itself and no just the path
  3. Cleanup the double headers on the way out (though they could be completely different, and that will probably just be the wrong answer).
Either way, let me know if any of these work for you

yishaigalatzer wrote Dec 13, 2013 at 11:28 PM

Here is another potential alternative:

var tokenCorsPolicy = new CorsPolicy
{
AllowAnyOrigin = true,
AllowAnyHeader = true,
AllowAnyMethod = true
};

var corsOptions = new CorsOptions
{
PolicyProvider = new CorsPolicyProvider
{
    PolicyResolver = request => Task.FromResult(request.Path.ToString().StartsWith("/token") ? tokenCorsPolicy : null)
}
};

app.UseCors(corsOptions);

var webApiConfig = new HttpConfiguration();
app.UseWebApi(webApiConfig);
3:31 PM

Ncage1974 wrote Dec 16, 2013 at 2:40 PM

Hi, i decided to try your last option and it worked perfectly. You still might want to consider though changing how that works. I'm sure there will be more people in the future that hit this problem. Anyways, thanks for your help :).