76

The scenario is simple, I need to log in from another server (different from the API server) to retrieve the access token.

I installed Microsoft.Owin.Cors package on the API Server. In Startup.Auth.cs file, under public void ConfigureAuth(IAppBuilder app), I added in

app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);

In WebApiConfig.cs, under public static void Register(HttpConfiguration config), I added in these lines:

// Cors
var cors = new EnableCorsAttribute("*", "*", "GET, POST, OPTIONS");
config.EnableCors(cors);

What else should I change?

Blaise
  • 21,314
  • 28
  • 108
  • 169
  • 6
    app.UseCors(CorsOptions.AllowAll) - enables CORS for all cross-origins requests to your site. config.EnableCors(..) enables CORS for Web Api only – Konstantin Tarkus Apr 14 '14 at 13:21

11 Answers11

81

Look at what I have found!

Add in some custom headers inside <system.webServer>.

<httpProtocol>
  <customHeaders>
    <add name="Access-Control-Allow-Origin" value="*" />
    <add name="Access-Control-Allow-Methods" value="GET, POST, OPTIONS, PUT, DELETE" />
  </customHeaders>
</httpProtocol>

Then I can do the CORS authentication.

spottedmahn
  • 14,823
  • 13
  • 108
  • 178
Blaise
  • 21,314
  • 28
  • 108
  • 169
  • 4
    I have no idea how I would have figured this out without your post; thank you! The web API documentation doesn't mention these configuration requirements...http://goo.gl/s9K6o2 & http://goo.gl/G8jKG0 – TheFastCat Dec 10 '13 at 22:13
  • 2
    Unfortunately, as much as people are saying not to do this, something along the OWIN authentication pipeline is 1) Overriding any response I've already written via context.Request.Headers as part of OWIN middleware 2) Even with custom policies configured, ignoring OWIN cors for the "/token" request. At least for testing purposes, I'm not sure I have much a choice for now. –  Mar 02 '15 at 04:31
  • 1
    I think what really should happen is, we should wrap token method to account controller with something called login, then the standard cors configuration would do the job. – Tim Hong May 04 '15 at 19:33
  • if you have app.UseCors(CorsOptions.AllowAll) , you only need to add Access-Control-Allow-Methods – maxisam Jun 12 '15 at 14:06
  • 8
    This is not a great solution. It works but it is the equivalent of turning off your firewall instead of configuring it properly. – Piotr Stulinski Sep 15 '15 at 20:23
  • 2
    I disagree. CORS isn't a solution to anything. A firewall protects the client, but CORS is (mis)used as a way to protect the server, but a simple client change of the request headers and you are golden. Just use e.g. chrome --disable-web-security flag or search for Allow-Control-Allow-Origin: * Chrome extension. – Peheje Mar 11 '16 at 20:10
  • 1
    Why is this so vehemently upvoted, this is NOT a solution. This completely overrides any CORS middleware configuration. – Mardoxx Sep 23 '16 at 18:40
  • @Mardoxx Would one of you care to share the correct solution? – Hades Dec 31 '16 at 15:21
  • @Mardoxx lot at my answer below. – Piotr Stulinski Aug 09 '17 at 15:00
  • So what's the damn solution then? – Scott Wilson Mar 28 '18 at 22:03
  • As I needed to get something quick and dirty working fast, this was very useful. I had to add `Content-Type` to the header list to get it to work with `fetch` though. – stuartd Sep 12 '18 at 09:18
52

I had many trial-and-errors to setup it for AngularJS-based web client.
For me, below approaches works with ASP.NET WebApi 2.2 and OAuth-based service.

  1. Install Microsoft.AspNet.WebApi.Cors nuget package.
  2. Install Microsoft.Owin.Cors nuget package.
  3. Add config.EnableCors(new EnableCorsAttribute("*", "*", "GET, POST, OPTIONS, PUT, DELETE")); to the above of WebApiConfig.Register(config); line at Startup.cs file.
  4. Add app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll); to the Startup.Auth.cs file. This must be done prior to calling IAppBuilder.UseWebApi
  5. Remove any xml settings what Blaise did.

I found many setup variations and combinations at here stackoverflow or blog articles. So, Blaise's approach may or may not be wrong. It's just another settings I think.

JDC
  • 1,569
  • 16
  • 33
Youngjae
  • 24,352
  • 18
  • 113
  • 198
  • 4
    This might create the following problem: http://aspnetwebstack.codeplex.com/workitem/1539 – Milen Kovachev Nov 13 '14 at 13:46
  • 2
    I have enabled CORS in my OWIN and Web API configs using `app.UseCors` and `config.EnableCors` methods, respectively, but the OWIN `/token` endpoint appears to be treated specially and is not sending the Access-Control-Allow-Origin HTTP Header in the response. This is true even when implementing the custom policy solution by Konstantin Tarkus below. –  Mar 02 '15 at 04:11
  • 1
    You need a Nuget package "Microsoft.AspNet.WebApi.Cors" to have classes and method for step 1. – Jan Zahradník Aug 29 '16 at 05:33
  • 3
    I had to install `Microsoft.Owin.Cors` as well – galdin Oct 25 '16 at 15:17
35

After many hours of searching and looking at many many different solutions to this i have managed to get this working as per the below.

There are a number of reasons this is happening. Most likely you have CORS enabled in the wrong place or it is enabled twice or not at all.

If you are using Web API and Owin Token end point then you need to remove all the references to CORS in your Web API method and add the correct owin method because web api cors will not work with Token endpoint whilst Owin cors will work for both Web API and Token auth end points so lets begin:

  1. Make sure you have the Owin Cors package installed Remove any line that you have eg.config.EnableCors(); from your WebAPIconfig.cs file

  2. Go to your startup.cs file and make sure you execute Owin Cors before any of the other configuration runs.

    app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll); ConfigureAuth(app);

  3. If your still having problems go to: Startup.Auth.cs and ensure you have the following in your ConfigureAuth method (you shouldnt need this if your startup.cs file is correct)

app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);

Piotr Stulinski
  • 9,241
  • 8
  • 31
  • 46
  • 2
    Thanks @Piotr, This is the real working answer. Can Blaise please mark this as the real answer? with all other answers, either OWIN /Token OR WebAPI works while they can't work together. In my case, adding XML in Web.config plus config.EnableCors(...) causes duplicated header [Access-Control-Allow-Headers] being added, which is prohibited in Chrome. – Joe Lau Dec 03 '16 at 09:54
  • I too face faced duplicate header issue after including both OWIN and Web API Cors in the project. But as @Piotr stated in the answer, only OWIN CORS is enough to take care of it. – Dipendu Paul Dec 21 '16 at 18:14
  • 1
    This is the best answer Thank you very much Additionally you may need Microsoft.AspNet.WebApi.OwinSelfHost package and Microsoft.Owin.Cors package to use UseCors Function of IAppBuilder (app) – janaka aravinda Mar 02 '18 at 05:42
23

web.config

<appSettings>
  <add key="cors:Origins" value="*" />
  <add key="cors:Headers" value="*" />
  <add key="cors:Methods" value="GET, POST, OPTIONS, PUT, DELETE" />
</appSettings>

Startup.cs

var appSettings = WebConfigurationManager.AppSettings;

// If CORS settings are present in Web.config
if (!string.IsNullOrWhiteSpace(appSettings["cors:Origins"]))
{
    // Load CORS settings from Web.config
    var corsPolicy = new EnableCorsAttribute(
        appSettings["cors:Origins"],
        appSettings["cors:Headers"],
        appSettings["cors:Methods"]);

    // Enable CORS for ASP.NET Identity
    app.UseCors(new CorsOptions
    {
        PolicyProvider = new CorsPolicyProvider
        {
            PolicyResolver = request =>
                request.Path.Value == "/token" ?
                corsPolicy.GetCorsPolicyAsync(null, CancellationToken.None) :
                Task.FromResult<CorsPolicy>(null)
        }
    });

    // Enable CORS for Web API
    config.EnableCors(corsPolicy);
}

Note: app.UserCors(...) should be called before configuring ASP.NET Identity.

Source: ASP.NET Web Application Starter Kit (ASP.NET Web API, Identity, SignalR)

Konstantin Tarkus
  • 37,618
  • 14
  • 135
  • 121
  • 1
    I've implemented this and am surprised to find that, verifying policy code execution is valid in the debugger, there is no Access-Control-Allow-Origin header in the HTTP response for the "/token" request. –  Mar 02 '15 at 04:09
  • Strangely, after enabling CORS via web.config by adding the response header manually, not through any OWIN or Web API CORS configuration, then removing that and re-enabling my API configurations, the problem with "/token" went away. Not sure what's going on, but the issue's gone now. –  Mar 02 '15 at 05:38
  • 1
    Keep in mind that the order where you place `.EnableCors()` in your OWIN pipeline does matter. You want to put it before other OWIN middleware stuff. – Konstantin Tarkus Mar 03 '15 at 11:03
  • is this the web.config for the api website or for the client website? – Manuel Hernandez Mar 29 '16 at 22:43
  • @KonstantinTarkus Hi, I want to only allow http requests from certain url. what should I put in place of value in . The only url that can make request is localhost:777. ? – user786 Nov 15 '16 at 12:16
16

To elaborate on Youngjae's answer, there's a great tutorial on setting up OWIN with Web API and enabling CORS in the process at http://bitoftech.net/2014/06/01/token-based-authentication-asp-net-web-api-2-owin-asp-net-identity/

You'll want to add the NuGet package for CORS with the command:
Install-Package Microsoft.Owin.Cors -Version 2.1.0

Then add
app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);

to your Configuration method in Startup.cs so it looks something like:

public void Configuration(IAppBuilder app)
{
    HttpConfiguration config = new HttpConfiguration();
    ConfigureOAuth(app);
    WebApiConfig.Register(config);
    app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
    app.UseWebApi(config);
}
TT.
  • 15,774
  • 6
  • 47
  • 88
Yaron
  • 1,867
  • 20
  • 16
  • 9
    This does not work for me. And I think the original question was exactly about this why app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll); does not work. – Milen Kovachev Nov 12 '14 at 09:31
  • I have enabled CORS in my OWIN and Web API configs using `app.UseCors` and `config.EnableCors` methods, respectively, but the OWIN `/token` endpoint appears to be treated specially and is not sending the Access-Control-Allow-Origin HTTP Header in the response. This is true even when implementing the custom policy solution by Konstantin Tarkus below. –  Mar 02 '15 at 04:12
9

The answer for me was found at

Web Api 2 Preflight CORS request for Bearer Token

Specifically, the /Token request using an implementation of OAuthAuthorizationServerProvider.GrantResourceOwnerCredentials was adding the header in again. Add the OWIN CORS stuff before any other OWIN config, and remove the header from GrantResourceOwnerCredentials, as per that link, and voila. Good luck.

Community
  • 1
  • 1
Dale Holborow
  • 560
  • 3
  • 19
9

I just want to share my experience. I spent half of the day banging my head and trying to make it work. I read numerous of articles and SO questions and in the end I figured out what was wrong.

The line

app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);

was not the first one in Startup class Configuration method. When I moved it to the top - everything started working magically.

And no custom headers in web.config or config.EnableCors(corsPolicy); or anything else was necessary.

Hope this will help someone to save some time.

Andrew
  • 412
  • 5
  • 16
  • 1
    saved me 30 mins before my assignment was due :) thanks! #magic lol – Norman Bentley Sep 15 '16 at 03:36
  • Even if I add this as the 1st line in the startup class configuration method, I get the error. But when I add the config along with this line it starts throwing the duplicate issue. – bomaboom Dec 15 '17 at 10:39
4

You can find here the multiple ways you have to enable CORS at different scopes: http://www.asp.net/web-api/overview/security/enabling-cross-origin-requests-in-web-api

Anyway I had the same issue, and by adding the headers in different ways didn't get the complete solution.

I found out that the IIS uses handlers which overrides your CORS web app config if you don't specify the opposite.

In my case, I also had to remove the usage of IIS handlers by adding the following configuration at the main Web.config of my application:

<system.webServer>
    <handlers>
      <remove name="ExtensionlessUrlHandler-Integrated-4.0" />
      <remove name="OPTIONSVerbHandler" />
      <remove name="TRACEVerbHandler" />
      <add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="*" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" />
    </handlers>
</system.webServer>

Be aware that this configuration may be set by default when you create a new project depending on the type of it, but if you start from scratch you probably will have to add this config.

TT.
  • 15,774
  • 6
  • 47
  • 88
Seba Cervantes
  • 199
  • 1
  • 7
2

Adding customer Headers might not give you as much freedom in customize your security needs. It opens up all other part of the api to the world. The following code only does that for "token", and controller other part of api should be done via EableCors annotation.

public void ConfigureAuth(IAppBuilder app)
{
    //other stuff
    app.Use(async (context, next) =>
    {
        IOwinRequest req = context.Request;
        IOwinResponse res = context.Response;
        if (req.Path.StartsWithSegments(new PathString("/Token")))
        {
            var origin = req.Headers.Get("Origin");
            if (!string.IsNullOrEmpty(origin))
            {
                res.Headers.Set("Access-Control-Allow-Origin", origin);
            }
            if (req.Method == "OPTIONS")
            {
                res.StatusCode = 200;
                res.Headers.AppendCommaSeparatedValues("Access-Control-    Allow-Methods", "GET", "POST");
                res.Headers.AppendCommaSeparatedValues("Access-Control-    Allow-Headers", "authorization", "content-type");
                return;
            }
        }
        await next();
    });
    //other stuff
}

To enable Cors, follow the instruction here.

TT.
  • 15,774
  • 6
  • 47
  • 88
Tim Hong
  • 2,734
  • 20
  • 23
1

When using OWIN middleware to handle CORS, we do not need to add headers on the WebAPIConfig or the web.config file. Yes, adding the headers on the web.config file does work when you want public access, but if you need to limit the access based on a whitelist (domains), then allowing All access is no longer what you would like to do.

With OWINS, we can handle this by implementing this handler:

OAuthAuthorizationServerProvider.MatchEndpoint

With this handler, we can detect the request method (OPTIONS, POST...) and if the request should be treated as an Authorize or Token endpoint. This is the area where logic can be added to check the Origin header (request) and validate if this domain should be allowed by adding the response header Access-Control-Allow-Origin.

string origin = context.Request.Headers.Get("Origin");
var found = IsDomainAllowed(origin);
 if (found){
      context.Response.Headers.Add("Access-Control-Allow-Origin",
                             new string[] { origin });
 }      

For more background on this, look at this link: http://www.ozkary.com/2016/04/web-api-owin-cors-handling-no-access.html

TT.
  • 15,774
  • 6
  • 47
  • 88
ozkary
  • 2,436
  • 1
  • 21
  • 20
0

Complete Soluction. You just need change some files, works for me.

Global.ascx

public class WebApiApplication : System.Web.HttpApplication {
    protected void Application_Start()
    {
        WebApiConfig.Register(GlobalConfiguration.Configuration);
    } }

WebApiConfig.cs

All the request has call this code.

public static class WebApiConfig {
    public static void Register(HttpConfiguration config)
    {
        EnableCrossSiteRequests(config);
        AddRoutes(config);
    }

    private static void AddRoutes(HttpConfiguration config)
    {
        config.Routes.MapHttpRoute(
            name: "Default",
            routeTemplate: "api/{controller}/"
        );
    }

    private static void EnableCrossSiteRequests(HttpConfiguration config)
    {
        var cors = new EnableCorsAttribute(
            origins: "*", 
            headers: "*", 
            methods: "*");
        config.EnableCors(cors);
    } }

Some Controller

Nothing to change.

Web.config

You need add handlers in you web.config

<configuration> 
  <system.webServer>
    <handlers>
      <remove name="ExtensionlessUrlHandler-Integrated-4.0" />
      <remove name="OPTIONSVerbHandler" />
      <remove name="TRACEVerbHandler" />
      <add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="*" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" />
    </handlers>   
  </system.webServer> 
</configuration>
TT.
  • 15,774
  • 6
  • 47
  • 88
Jonas Ribeiro
  • 306
  • 2
  • 3