Getting 'Access-Control-Allow-Origin' error when I call External WebApi - c#

I found a lot of pages regards to this issue, but none of the Solutions worked.
I have a JavaScript Button in the View that is calling a WebApi method(C#)
$('#btn-Sign-in', this).click(function () {
var apiUrl = 'https://localhost:44391/api/test';
fetch(apiUrl).then(response => {
return response.json();
}).then(data => {
// Work with JSON data here
console.log(data);
}).catch(err => {
// Do something for an error here
});
});
This is My Webapi method that is calling external api:
[RoutePrefix("api")]
public class TestController : ApiBaseController
{
[HttpGet]
[Route("redirectforauth")]
[RequiresPermission("Home:View")]
[HttpGet]
[Route("test")]
[RequiresPermission("Home:View")]
public async Task<IHttpActionResult> ConnectExternal()
{
var request = new FlurlRequest("URL of an external website")
.SetQueryParam(...)
.SetQueryParam(...)
.SetQueryParam(...)
.SetQueryParam(...);
var redirectUrl = request.Url.ToInvariantString();
return Redirect(redirectUrl);
}
when I am running the project, I am getting this error:
CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.

This error is due to your web application URL & C# API URL are not having same origins (not running on same port or same hosts). Two URLs have the same origin if they have identical schemes, hosts, and ports.
EX: Your C# URL https://localhost:44391 is different from your web application URL.
First, to make sure that you are getting the error due to your c# api & not due to the external api, comment out your external api request code inside your action, and return something directly from your api to web application. (If the error is from external Api, that external api needs to handle this to allow access to other origins.)
If you are getting the error due to your c# api & not due to the external api, one approach to do this is, by enabling CORS in your C# WebApi application. You can enable CORS per action, per controller, or globally for all Web API controllers in your application.
You can install this nuget package in your C# API application:
Microsoft.AspNet.WebApi.Cors
In App_Start/WebApiConfig.cs, add this code to the WebApiConfig.Register method:
config.EnableCors();
Next, add the [EnableCors] attribute to your TestController class, with your web application URL.
Ex: if your web application URL is localhost:3000, add it in origins as below.
[EnableCors(origins: "http://localhost:3000", headers: "*", methods: "*")]
public class YourController : ApiController {
// Your methods...
}
Now the AJAX request from your Web Client should work. The GET, PUT, and POST methods are all allowed.
For more details on how to enable CORS & how it works:
Enable cross-origin requests in ASP.NET Web API 2

Related

ASP.NET Core Minimal API returns 404 code instead of 401 for unauthenticated requests to unmapped URLs

I'm implementing a simple ASP.NET REST API with authentication. Authentication is a custom AuthenticationHandler implementing basic auth like described here: https://dotnetthoughts.net/implementing-basic-authentication-in-minimal-webapi/. I'm also registering the DefaultAuthenticationScheme as described in this StackOverflow answer: https://stackoverflow.com/a/69171141/1147926.
Everything works as intended except when hitting an unmapped URL with an unauthenticated HTTP request. In this case the server returns a 404 error. I would expect a 401 response.
Note that the response also contains a WWW-Authenticate header added by the auth handler, but its 401 response code gets overwritten later in the handler chain.
Is it possible to change this behavior in ASP.NET Core and stop request processing when a request couldn't be authenticated successfully?
I think what you really want to do is protect the entire site with an authorization policy.
builder.Services.AddAuthorization(options =>
{
options.FallbackPolicy = new AuthorizationPolicyBuilder().RequireAuthenticatedUser().Build();
});
This will get applied if there isn't another policy to apply to the endpoint.
The default behavior for ASP.NET Core is to return a 404 if an endpoint can't be mapped. However, you can change the default behavior by adding a simple piece of middleware in the startup code of your web application.
Something like this should work:
app.Use(async (context, next) =>
{
await next();
if (context.Response.StatusCode == (int)HttpStatusCode.NotFound
&& context.Request.Path.StartsWithSegments("/authenticated-base-path"))
{
context.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
}
});

DenyAnonymousAuthorizationRequirement Error in Docker but works in Development

I am getting the DenyAnonymousAuthorizationRequirement when trying to call a public API. Most of the API endpoints are secure but three are publicly available.
These three public APIs are called on the home page before the user logs in and surely this is not a unique situation so there must be a way you can have both in an application.
I am NOT seeing this error in the development environment but I am seeing it when I deploy to my docker container. I understand that DenyAnonymousAuthorizationRequirement is a part of the .NET core Policy-based authorization model.
What I understand from this article is I can specify endpoints to NOT apply the DenyAnonymousAuthorizationRequirement. This however is not working for me so either I am doing it wrong or this article doesn't apply to my situation.
Fortunately I am also in control of the API so I can provide the implementation.
[Route("[controller]")]
[ApiController]
public class MappingController : ControllerBase
{
[HttpGet]
[Route("businesses")]
[AllowAnonymous]
public async Task<IActionResult> GetBusinesses()
{
var result = await _mappingService.GetAllBizMapMarkersAsync();
return Ok(result);
}
}
In the app where I am getting the error I have specified in startup.cs what I thought would be an override for that endpoint based upon this article.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute("Mapping", "/businesses").AllowAnonymous();
});
}
The app calls the API like this.
var response = await _client.GetAsync($"mapping/businesses");
This is the error.
[13:39:34 INF] Start processing HTTP request GET https://devapi.mysite.com/mapping/businesses
[13:39:34 INF] Sending HTTP request GET https://devapi.mysite.com:5150/mapping/businesses
[13:39:34 INF] Authorization failed. These requirements were not met:
DenyAnonymousAuthorizationRequirement: Requires an authenticated user.
What can I try next?

Consume third party API with Blazor WebAssembly [duplicate]

I have a blazor web assembly which fetches from an external API built on ASP.NET Core which I do not have access to. I can perform get requests, but cannot perform post requests. I get the following error when I do.
Access to fetch at 'http://external:9000/User/Create' from origin 'http://localhost:56138' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
The author of the api confirms he enabled cors to allow any header in his startup and also suggested I do the same but this did not fix the issue. I confirmed from the debugger I am sending the right data format the endpoint requires and I am also running on http scheme same as the web service.
This is the client configuration in program.cs
builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri("http://external:9000/") });
builder.Services.AddCors(policy =>
{
policy.AddPolicy("_myAllowSpecificOrigins", builder => builder.WithOrigins("http://external:9000/")
.AllowAnyMethod()
.AllowAnyHeader()
.AllowCredentials());
});
This is how I post
var dataJson = JsonConvert.SerializeObject(application);
var stringContent = new StringContent(dataJson, Encoding.UTF8, "application/json");
var response = await _httpClient.PostAsync($"User/Create", stringContent);
I have read this is a common problem with blazor assembly, I'm not entirely sure of what I read. I am currently trying to move the project to blazor server to see if it would work, but I would prefer it on web assembly.
builder.Services.AddCors(policy =>
{
policy.AddPolicy("_myAllowSpecificOrigins", builder => builder.WithOrigins("http://external:9000/")
.AllowAnyMethod()
.AllowAnyHeader()
.AllowCredentials());
});
This configuration should be done on a Server, and not yours but the server of the external API. You do nothing in that regard, except call end points on that Web Api.
The author of the api confirms they enabled cors to allow any header in his startup
If so, ask them for the code for confirmation...
and also suggested I do the same but this did not fix the issue.
You do nothing of the sort.
Workaround solution:
AS CORS is a security feature of JavaScript enforced by the browser, you can circumvent it by calling your Server code from which you perform the call to this Web Api end point, and then returns it back to your WebAssembly front-end. Use asynchronous code as much as you can.
Update as per comment
Are you saying I should have two projects, the server and the client under one solution? The server calls the calls the external api, then passes it to the client. Is this what your last suggestion is?
If you're using WebAssembly Blazor App hosted wherein the hosting server contains Web Api controllers, then you should expose end points that can be called from your WebAssembly front-end. The code in these end points should perform the HTTP calls to the external Web Api, and pass back to the WebAssembly calling methods the data received from the external Web Api.
Note: If you don't have such controllers ( they are created by default by Visual Studio), you may add them yourself to the server project.
If you already have created a Web Api project instead of those controllers, then expose the necessary end points from your Web Api project. Note that it makes no difference whether your Web Api project resides in the same solution of the WebAssembly front-end, as long as you provide the correct Url.
If you're using WebAssembly Blazor App stand alone; that is, the default installation does not create a Server project, you'll need to create a Web Api project and use it, unless you've already created one.
You are working with localhost so you should use this configuration to your Server:
builder.Services.AddCors(policy =>
{
policy.AddPolicy("_myAllowSpecificOrigins", builder =>
builder.WithOrigins("http://localhost:56138/")
.SetIsOriginAllowed((host) => true) // this for using localhost address
.AllowAnyMethod()
.AllowAnyHeader()
.AllowCredentials());
});
//
app.UseCors("_myAllowSpecificOrigins");

"Method Not Allowed(#405 status code)" While Putting Data on Web api from Console Application in C#

I am working on Console Application and, I am calling a web API from console application to put data onto the web API but when I am getting the data using GetAsync() method I am able to get the data and when I am putting the data also able to put data while the web API is not hosted on IIS
After hosting on IIS I got the status code 405 "Method not allowed"
I have also tried enabling cors inside web API config in a register()
config.EnableCors();
and
[EnableCors(origins: "*", headers: "*", methods: "*")]
public class ValuesController : ApiController
Any help will be Appreciated.
Thanks
I was putting the data onto Web API using PutJsonAsAsync(), but It doesn't work with PutJsonAsAsync() I don't know why but when I used PostAsJsonAsync() it works for me.
thanks

Web API CORS FormsAuthentication from AngularJS Client

I Implemented an AngularJS Project. The Server Side I coded using .NET Web API (C#). I Enabled the CORS in the Web API by installing the NuGet Package https://www.nuget.org/packages/Microsoft.AspNet.WebApi.Cors
[EnableCors(origins: "*", headers: "*", methods: "*")]
public class SampleController : ApiController
{
[AllowAnonymous]
[HttpGet]
public string GetAnonymousString()
{
FormsAuthentication.SetAuthCookie("Sample AllowAnonymous", false);
return "Calling AllowAnonymous Method using CORS - Public Text";
}
[Authorize]
[HttpGet]
public string GetAuthorizeString()
{
return "Calling Authorize Method using CORS - Private Text";
}
}
I hosted the client Side Application in http://localhost:8050 and I hosted Service Web API in http://localhost:8060.
Now I tried to access the GetAnonymousString() - http://localhost:8060/api/Sample/GetAnonymousString its working fine and also it returns the httponly cookie to the client via response.
But I tried the same for GetAuthorizeString() - http://localhost:8060/api/Sample/GetAuthorizeString, it returns 401 Unauthorized error as a response.
If I hosted Client App and Web API in the same domain, then its working fine. Only issue is CORS.
Kindly assist me how to use the Authorize in CORS ???

Categories