Cors issues on WebAPI - c#

Enabling Cors on WebAPI
I have this set in WebApiConfig.cs
config.EnableCors();
and this is how my attribute is setup for my controller method:
[EnableCors("http://dev.example.com,http://personal.example.com,http://www.example.com", // Origin
"Accept, Origin, Content-Type, Options", // Request headers
"POST", // HTTP methods
PreflightMaxAge = 600 // Preflight cache duration
)]
But I still get the error: "The 'Access-Control-Allow-Origin' header contains multiple values."
What else do I need to do to prevent this? We must allow from all three domains. but the first 2 are sub-domains of the last one.

do you have any options set into your web.config file for cors ? i.e something like <add name="Access-Control-Allow-Origin" value="*"/>
if yes make sure to remove that, and control the cors through the code only.
Edit:
well, that means that you always add the header to your response, no matter which controller the request hits, and in case the request hits the controller with the EnableCors attribute it will add another header. If you removed the one in the Application_BeginRequest() it should work, however that means that you need to decorate all other controllers with EnableCors attribute, which maybe acceptable in your case, otherwise, you need to add a DelegateHandler where you can check the request and set the cors depending on the requested controller. have a look at this http://georgedurzi.com/implementing-cross-browser-cors-support-for-asp-net-web-api/ it may help start with DelegateHandlers. Hope that helps.

Related

Authentication Handler not blocking requests

I have added authentication to my API with the possibility to authenticate with two different authentication schemes.
Based on the format of the Auth header I forward the authentication request to the appropriate Authentication handler using a ForwardDefaultSelector.
services.AddAuthentication(opt =>
{
opt.DefaultScheme = "ForwardScheme";
opt.DefaultChallengeScheme = "ForwardScheme";
})
.AddPolicyScheme("ForwardScheme", "ForwardScheme", options =>
options.ForwardDefaultSelector = context =>
context.Request.IsSchemeA()
? "SchemeA"
: "SchemeB")
.AddSchemeA()
.AddSchemeB();
Adding Schemes:
public static AuthenticationBuilder AddSchemeA(this AuthenticationBuilder builder)
{
builder.AddScheme<AuthenticationSchemeOptions, SchemeAHandler>(
"SchemeA", null);
return builder;
}
The forwarding seems to be working fine, I can see the request coming to the right auth handler based on the header value.
The problem is even when the auth fails, the API call is not blocked and I still get a 200 response back.
In the AuthHandler I am just returning this:
return AuthenticateResult.Fail("Authentication Failed");
Any idea what I am missing here?
Thanks.
If you register an authentication scheme for your application and you add the authentication middleware to the ASP.NET core request pipeline, you are basically asking the ASP.NET core framework of trying to authenticate any incoming request, by using the specified authentication scheme. This won't change, by itself, the response status code from 200 to 401.
In order for you to get a 401 response when an anonymous request gets to your server, you need to raise a so called authetication challenge to the incoming request.
The simplest way to do that is basically requiring the request principal to be authenticated in order to execute a certain action method. To do that you simply need to decorate the action method by using the [Authorize] attribute. This way you are setting an execution policy to your action method, which allows the method execution only if the request principal is authenticated.
#EnricoMassone thanks for pointing me in the right direction.
I was missing [Authorize] attribute on my controller methods.
you can set the attribute individually on each method or you could do something like this, and it would enable authorization on all methods for all of your controllers

Enable preflight CORS between C# and Angular

I am trying to make CORS work for request that require a preflight check. In this case I am making a POST-request to the back-end with an extra header.
Angular:
let myHeaders = new HttpHeaders();
myHeaders = myHeaders.append('Content-Type', 'application/json');
return this.http.post<UserOrder>(`${this.apiURL}/Order/PlaceOrder`, JSON.stringify(payload), {headers : myHeaders}); //email);
C# API:
[HttpPost("PlaceOrder")]
public GenericResponse PlaceOrder(UserOrderInsertModel userOrder)
{
return _orderProvider.PlaceOrder(new UserOrder());
}
Because of the preflight check it first makes an OPTIONS-request. When I do not define a separate options-endpoint in the backend I get a 405 Method Not Allowed. This made me think I needed a separate options-endpoint in my back-end on top of the post-endpoint.
[HttpOptions("PlaceOrder")]
public ActionResult PlaceOrderOptions(UserOrderInsertModel userOrder)
{
return Ok();
}
After adding this I run into a 415 Unsupported Media Type (on the options call). This is probably because the Content-Type header is not supported for a HttpOptions request.
I feel like the extra options endpoint shouldt be neccessary at all. The CORS-middleware I currently use is as follows:
httpContext.Response.Headers.Add("Access-Control-Allow-Origin", "http://localhost:4200");
httpContext.Response.Headers.Add("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
httpContext.Response.Headers.Add("Access-Control-Allow-Headers", "*");
httpContext.Response.Headers.Add("Access-Control-Allow-Credentials", "true");
Extra info: the CORS in general did already work. Cross-site scripting with a GET request and the Access-Control-Allow-Origin header went well. It is just that I cannot get the POST/OPTIONS combo to work.
Edit: in the Startup.cs I first tried to use app.UseCors() as follows:
app.UseCors();
options => options.WithOrigins("http://localhost").AllowAnyMethod()
);
This unfortuantely didnt work so then I resorted to inserting the middleware as described above.
Any help would be highly appreciated!
Ok, thanks a lot everybody. The problem was most likely in the middleware I was using. This was a workaround I added because the UseCors() initially didnt work. This was probably because I didnt use app.AddCors() initially. Without the custom middleware it makes everything a lot easier!
A quick look at the documentation will clarify few things. I share few extracts below
Because of the preflight check it first makes an OPTIONS-request. When I do not define a separate options-endpoint in the backend I get a 405 Method Not Allowed. This made me think I needed a separate options-endpoint in my back-end on top of the post-endpoint.
Preflight Request
For some CORS requests, the browser sends an additional OPTIONS request before making the actual request. This request is called a preflight request.
The browser can skip the preflight request if all the following conditions are true
The request method is GET, HEAD, or POST.
The app doesn't set request headers other than Accept,
Accept-Language, Content-Language, Content-Type, or Last-Event-ID.
The Content-Type header, if set, has one of the following values:
application/x-www-form-urlencoded multipart/form-data text/plain
I feel like the extra options endpoint shouldt be neccessary at all.
Except you are using CORS with endpoint routing, ASPNET Core should respond to appropriate preflight request when core is enabled on startup.
Condition for Automatic preflight
When the CORS policy is applied either:
Globally by calling app.UseCors in Startup.Configure.
Using the [EnableCors] attribute.
ASP.NET Core responds to the preflight
OPTIONS request.
Enabling CORS on a per-endpoint basis using RequireCors currently does not support automatic preflight requests.
Enable Cors on Starup
In configure service
public void ConfigureServices(IServiceCollection services)
{
.
//other codes
.
services.AddCors(options =>
{
options.AddDefaultPolicy(
builder => //check corsbuilder for additonal config
{
builder.WithOrigins("http://example.com",
"http://www.contoso.com;
});
});
.
//other codes
.
}
and in Configure method
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
.
//Other codes
.
app.UseCors();
.
//Other codes
.
}
full documentation here for 3.0 https://learn.microsoft.com/en-us/aspnet/core/security/cors?view=aspnetcore-3.0#ecors
Enables Cors and make sure to AllowAnyHeader and AllowAnyMethod
Using .Net Core
services.AddCors(options =>
{
options.AddDefaultPolicy(builder =>
{
builder.WithOrigins(Configuration.GetValue<string>("JwtConfig:corsWhiteListUrl"))
.AllowAnyHeader()
.AllowAnyMethod();
});
});
Make sure to call UseCors() before UseEndpoints().
Another hint: If you have credentials, the wildcard do not work as expected.
https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Methods

How to allow CORS for ASP.NET WebForms endpoint?

I am trying to add some [WebMethod] annotated endpoint functions to a Webforms style web app (.aspx and .asmx).
I'd like to annotate those endpoints with [EnableCors] and thereby get all the good ajax-preflight functionality.
VS2013 accepts the annotation, but still the endpoints don't play nice with CORS. (They work fine when used same-origin but not cross-origin).
I can't even get them to function cross-origin with the down and dirty
HttpContext.Current.Response.AppendHeader("Access-Control-Allow-Origin", "*");
approach -- my browsers reject the responses, and the cross-origin response headers don't appear.
How can I get CORS functionality in these [WebMethod] endpoints?
I recommend double-checking you have performed all steps on this page: CORS on ASP.NET
In addition to:
Response.AppendHeader("Access-Control-Allow-Origin", "*");
Also try:
Response.AppendHeader("Access-Control-Allow-Methods","*");
Try adding directly in web config:
<system.webServer>
<httpProtocol>
<customHeaders>
<add name="Access-Control-Allow-Methods" value="*" />
<add name="Access-Control-Allow-Headers" value="Content-Type" />
</customHeaders>
</httpProtocol>
</system.webServer>
Failing that, you need to ensure you have control over both domains.
FYI, enable CORS in classic webform. In Global.asax
void Application_Start(object sender, EventArgs e)
{
GlobalConfiguration.Configuration.EnableCors();
RouteTable.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = System.Web.Http.RouteParameter.Optional }
);
If you need the preflight request, e.g. so you can send authenticated requests, you are not able to set Access-Control-Allow-Origin: *. It must be a specific Origin domain.
Also you must set the Access-Control-Allow-Methods and Access-Control-Allow-Headers response headers, if you are using anything besides the defaults.
(Note these constraints are just how CORS itself works - this is how it is defined.)
So, it's not enough to just throw on the [EnableCors] attribute, you have to set values to the parameters:
[EnableCors(origins: "https://www.olliejones.com", headers: "X-Custom-Header", methods: "PUT", SupportsCredentials = true)]
Or if you want to do things manually and explicitly:
HttpContext.Current.Response.AppendHeader("Access-Control-Allow-Origin", "https://www.olliejones.com");
HttpContext.Current.Response.AppendHeader("Access-Control-Allow-Headers", "X-Custom-Header");
HttpContext.Current.Response.AppendHeader("Access-Control-Allow-Methods", "PUT");
HttpContext.Current.Response.AppendHeader("Access-Control-Allow-Credentials", "true");
One last thing - you do have to call .EnableCors() on initiation. In e.g. MVC or WebAPI, you would call this on HttpConfiguration, when registering the config and such - however I have no idea how it works with WebForms.
If you use the AppendHeader method to send cache-specific headers and at the same time use the cache object model (Cache) to set cache policy, HTTP response headers that pertain to caching might be deleted when the cache object model is used. This behavior enables ASP.NET to maintain the most restrictive settings. For example, consider a page that includes user controls. If those controls have conflicting cache policies, the most restrictive cache policy will be used. If one user control sets the header "Cache-Control: Public" and another user control sets the more restrictive header "Cache-Control: Private" via calls to SetCacheability, then the "Cache-Control: Private" header will be sent with the response.
You can create a httpProtocol in web config for customHeaders.
<httpProtocol>
<customHeaders>
<add name="Access-Control-Allow-Methods" values="*" />
</customHeaders>
<httpProtocol>
I think your code looks good, but IIS does not send the header entity alone with response expectedly. Please check whether IIS is configured properly.
Configuring IIS6
Configuring IIS7
If CORS doesn't work for your particularity problem, maybe jsonp is another possible way.
For the web form, you can use
Response.AddHeader("Access-Control-Allow-Origin", "*");
instead of
Response.AppendHeader("Access-Control-Allow-Origin", "*");
The first one works for old version of ASP.Net Web Form.
You can do like this in MVC
[EnableCors(origins: "*", headers: "*", methods: "*")]
public ActionResult test()
{
Response.AppendHeader("Access-Control-Allow-Origin", "*");
return View();
}

Allow frame from different domain with MVC5

I am trying to load google maps into an iframe using MVC5 but I am getting blocked with the error
Refused to display 'https://www.google.com/maps?cid=XXXXX' in a frame because it set 'X-Frame-Options' to 'SAMEORIGIN'.
So after much searching, I have tried the following:
Adding AntiForgeryConfig.SuppressXFrameOptionsHeader = true; to the Application_Start in global.ascx
Creating an attribute (have tried this with and without the setting in global.ascx):
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
if (filterContext != null)
{
filterContext.HttpContext.Response.Headers["X-Frame-Options"] = "ALLOW-FROM https://www.google.com";
base.OnActionExecuted(filterContext);
}
}
trying the attribute OnResultExecuted(ResultExecutedContext filterContext) instead of OnActionExecuted
remove it in the web.config:
<httpProtocol>
<customHeaders>
<remove name="X-Frame-Options" />
</customHeaders>
</httpProtocol>
Is there something I'm missing? how do I get rid of this http header (or at least change it to allow maps)?
Update
I have just checked the headers being sent and they are correct in that they either say
X-Frame-Options: ALLOW-FROM https://www.google.com
Or aren't there at all if I remove the attribute but keep the global.ascx update
Yet when I run the page and see these headers, it is still giving me the SAMEORIGIN error.
As it turns out I have been completely stupid and misunderstood how x-frame-options work. It is to stop your site page being shown on another site through an iframe.
So the x-frame-options http header that I was getting for SAMEORIGIN was actually coming from google. I thought that as the url was returned from their places api I could just use it, but apparently you can only link to it.
Creating a new map api key and enabling the maps embed api, I was able to use the place_id instead and call the following url into the iframe:
https://www.google.com/maps/embed/v1/place?key=YOUR_API_KEY&q=place_id:PLACE_ID
And this would show without me getting the header (or doing anything extra to my headers).
I'll leave this here just in case anyone is as daft as I am

ASP.NET Web API CORS not working with AngularJS

I have an ASP.NET Web API running locally on some port and I have an angularjs app running on 8080. I want to access the api from the client.
I can successfully login and register my application because in my OAuthAuthorizationProvider explicitly sets the repsonse headers in the /Token endpoint.
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { "*" });
That's good. However, my other API methods do not seem to work. In my WebApiCongig.Register, I enable CORS and I add the EnableCors Attribute to my controllers to allow all origins, all headers, and all methods. I can set a break point in my get method on the controller and it gets hit just fine. Here is what I found watching the Network tab in chrome.
2 requests are are sent to the same api method. One method type OPTIONS and one with method type GET. The OPTIONS request header includes these two lines
Access-Control-Request-Headers:accept, authorization
Access-Control-Request-Method:GET
And the response includes these lines
Access-Control-Allow-Headers:authorization
Access-Control-Allow-Origin:*
However, the GET method request looks quite different. It returns ok with a status code of 200, but it does not inlcude and access control headers in the request or response. And like I said, it hits the API just fine. I can even do a POST and save to the database, but the client complains about the response!!
I've looked at every single SO question and tried every combination of enabling cors. I'm using Microsoft.AspNet.Cors version 5.2.2. I'm' using AngularJS version 1.3.8. I'm also using the $resource service instead of $http which doesn't seem to make a difference either.
If I can provide more information, please let me know.
BTW, I can access the Web API using Fiddler and/or Postman by simply including the Bearer token.
You don't seem to be handling the preflight Options requests.
Web API needs to respond to the Options request in order to confirm that it is indeed configured to support CORS.
To handle this, all you need to do is send an empty response back. You can do this inside your actions, or you can do it globally like this:
protected void Application_BeginRequest()
{
if (Request.Headers.AllKeys.Contains("Origin") && Request.HttpMethod == "OPTIONS")
{
Response.Flush();
}
}
This extra check was added to ensure that old APIs that were designed to accept only GET and POST requests will not be exploited. Imagine sending a DELETE request to an API designed when this verb didn't exist. The outcome is unpredictable and the results might be dangerous.
Also I suggest enabling Cors by web.config instead of config.EnableCors(cors);
This can be done by adding some custom headers inside the <system.webServer> node.
<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>
Please note that the Methods are all individually specified, instead of using *. This is because there is a bug occurring when using *.
This ended up being a simple fix. Simple, but it still doesn't take away from the bruises on my forehead. It seems like the more simple, the more frustrating.
I created my own custom cors policy provider attribute.
public class CorsPolicyProvider : Attribute, ICorsPolicyProvider
{
private CorsPolicy _policy;
public CorsPolicyProvider()
{
// Create a CORS policy.
_policy = new CorsPolicy
{
AllowAnyMethod = true,
AllowAnyHeader = true,
AllowAnyOrigin = true
};
// Magic line right here
_policy.Origins.Add("*");
}
public Task<CorsPolicy> GetCorsPolicyAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
return Task.FromResult(_policy);
}
}
I played around with this for hours. Everything should work right?? I mean the EnableCors attribute should work too?? But it didn't. So I finally added the line above to explicitly add the origin to the policy. BAM!! It worked like magic. To use this just add the attribute to your api class or method you want to allow.
[Authorize]
[RoutePrefix("api/LicenseFiles")]
[CorsPolicyProvider]
//[EnableCors(origins: "*", headers: "*", methods: "*")] does not work!!!!! at least I couldn't get it to work
public class MyController : ApiController
{
in my case, after I changed the Identity option of my AppPool under IIS from ApplicationPoolIdentity to NetworkService, CORS stopped working in my app.

Categories

Resources