Server uses Cors but UI still gets allow-origin error - asp.net-core

Hello i am trying to issue a http get request to a .NET Core Console App from my Angular 2 frontend and i get the following error:
Access to XMLHttpRequest at 'http://127.0.0.1:9300/api/getusers' from origin 'http://localhost:4200' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
For me it is curious since i have enabled CORS on the server side as you can see below in the Startup class.
Startup
public class Startup {
public Startup(IConfiguration configuration) {
Configuration = configuration;
}
public void ConfigureServices(IServiceCollection services) {
services.AddOptions();
services.AddMvc();
}
public IConfiguration Configuration;
public void Configure(IApplicationBuilder app) {
Console.WriteLine("request delievered");
Debug.WriteLine("Entered Server !");
app.UseMvc();
app.UseCors(x => { x.AllowAnyHeader(); x.AllowAnyOrigin();x.AllowAnyMethod(); });
}
}
I make the request from the UI like this:
#Injectable()
export class UserService{
private static baseUrl:string="http://127.0.0.1:9300/api";
constructor(private http:HttpClient) {
}
getClientsAsync():Promise<User[]>{
let route=UserService.baseUrl+"/getusers";
var data=(this.http.get(route) //should i have some headers here?
.map(resp=>resp)
.catch(err=>
Observable.throwError(err)
) as Observable<User[]>).toPromise<User[]>();
return data;
}
}
P.S I have tried with Postman and the request works ,however here in the angular 2 i have not included any headers for my http.get method.Could this be the problem ?

You need to put UseCors before UseMvc.
public void Configure(IApplicationBuilder app) {
Console.WriteLine("request delievered");
Debug.WriteLine("Entered Server !");
app.UseCors(x => { x.AllowAnyHeader(); x.AllowAnyOrigin();x.AllowAnyMethod(); });
app.UseMvc();
}
This is because UseCors adds a middleware (as does UseMvc), and middleware are executed in order from top to bottom. So the request never gets to the CORS middleware.

Related

How to return HTTP 404

I am building an asp.net core Web API and I need to be able to hide some of the actions in a controller.
I use the following code to return HTTP 404 (Not Found):
[HttpGet]
public IActionResult Index()
{
if(!_isEnabled)
{
return NotFound();
}
However, in my browser I get this result:
{
"type": "https://tools.ietf.org/html/rfc7231#section-6.5.4",
"title": "Not Found",
"status": 404,
"traceId": "00-502319d62a6027718d2ee2cb3c9f263f-28de7bfdfb48f2d8-00"
}
I need to make the call as if the controller does not exists and the browser shows this:
How can a Controller returns a "real" HTTP 404 experience as if the controller dos not exists at that route?
Update 1
The answers return a JSON data and response code 404.
I am trying to do something different.
I am trying to hide the controller as if it doesn't exist for security reasons. I like the end user browser see above screenshot (Edge in my example)
Update 2
I changed to the following code:
[HttpGet]
[ProducesResponseType(StatusCodes.Status404NotFound)]
[ProducesResponseType(StatusCodes.Status200OK)]
public IActionResult Index()
{
if(!_isEnabled)
{
return StatusCode(StatusCodes.Status404NotFound);
}
and the controller returns the following result:
{"type":"https://tools.ietf.org/html/rfc7231#section-6.5.4","title":"Not Found","status":404,"traceId":"00-3275026575270e11a4b1a5ab0817776a-a4777e626460faeb-00"}
The behavior is strange. Is it a new feature in aspnet code 6 ?
Update 3
Here is my middleware setup in the Program.c. It is plain oob setup:
public static void Main(string[] args)
{
var builder = WebApplication.CreateBuilder(args);
builder.Logging.ClearProviders();
builder.Logging.AddConsole();
builder.Logging.AddAzureWebAppDiagnostics();
// Add services to the container.
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApi(builder.Configuration.GetSection("AzureAd"));
builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
builder.Services.AddApplicationInsightsTelemetry();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();
app.Run();
}
Solution For Update 1:
Middleware could be your savior here thus can be achived what you are trying to implement.
Controller:
[ProducesResponseType(StatusCodes.Status404NotFound)]
public IActionResult GetById(int id)
{
// return Ok(NotFound());
return StatusCode(StatusCodes.Status404NotFound);
}
Note: You can choose either of the status pattern.
Middleware:
public class CustomResponseMiddleware
{
private readonly RequestDelegate _next;
public CustomResponseMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task InvokeAsync(HttpContext httpContext)
{
if (httpContext.Response.StatusCode == 404)
{
httpContext.Response.Redirect("/WrongControllerName/WrongAction");
}
await _next(httpContext);
}
}
Note: As you can see, we are checking the controller status code and checking if any 404 decteced. Once the desired status code we will redirect a controller which doesn't exist at all that eventually generate the expected output.
Register Middleware In Program.cs:
app.UseMiddleware<CustomResponseMiddleware>();
Output:

CORS issue with PUT request from reactjs to asp.netCore api

I am working on a Reactjs app with Asp.netCore API with Sql database
deployed to IIS server.
All the operation is working except PUT operation on the task
it break with CORS error and the request is not reaching the backend, I tested it locally and it's working fine. Tested using Postman and it's working too even the deployed version is working with postman. I can't figure out what's the issue or from where I should start debugging.
startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddCors(o => o.AddPolicy(name: CorsPolicy, builder =>
{
builder.WithOrigins(FrontEnd_URL).SetIsOriginAllowed((host) =>
true).AllowAnyHeader().AllowAnyMethod().AllowCredentials();
}));
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseRouting();
app.UseCors(CorsPolicy);
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
endpoints.MapHub<TrelloHub>("/trello");
});
}
TaskController
[Route("/api/tasks")]
[ApiController]
public class TasksController : Controller
{
private readonly IMapper _mapper;
private readonly ITaskService _taskService;
public TasksController(ITaskService taskService , IMapper mapper)
{
_mapper = mapper;
_taskService = taskService;
}
[HttpPut("{id:int}", Name = "UpdateTask")]
public async Task<String> UpadateTask([FromBody]TaskDto taskdto, int id)
{
var taskModel = _mapper.Map<Task>(taskdto);
return await _taskService.UpadateTask(id , taskModel);
}
}
first check IIS server logs. it can help you a lot, also look at the request headers of the reactjs client origin: header exactly and add it to the allowed origins

CORS Error: No 'Access-Control-Allow-Origin' header is present on the requested resource

I am using .Net Core 3. I have programmed an API being called by a SPA hosted in a .Net Core Web Project.
For one POST action in the API, I get a CORS error in Chrome as well as Firefox while for another POST action in the same controller of the API, everything works fine.
The error that I get is
Access to fetch at 'https://subdomain1.domain1.com:50003/api/v1/projects/project' from origin
'https://subdomain2.domain2.com:50002' has been blocked by 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.
In the API,
In the startup class, I have following
readonly string MyAllowSpecificOrigins = "AllowOrigin";
public void ConfigureServices(IServiceCollection services)
{
CorsPolicyBuilder builder = new CorsPolicyBuilder();
CorsPolicy policy = builder.WithOrigins("https://subdomain2.domain2.com:50002")
.AllowAnyMethod()
.AllowAnyHeader()
.AllowCredentials()
.Build();
services.AddCors(options =>
{
options.AddPolicy(MyAllowSpecificOrigins, policy);
});
services.AddControllers();
services
.AddAuthentication("Bearer")
.AddJwtBearer(jwtOptions => {
jwtOptions.Authority = "https://subdomain.domain.com:50001";
jwtOptions.Audience = "portal-api";
jwtOptions.RequireHttpsMetadata = false;
});
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
var provider = new FileExtensionContentTypeProvider();
provider.Mappings[".hrc"] = "application/octet-stream";
provider.Mappings[".obj"] = "application/octet-stream";
app.UseHttpsRedirection();
app.UseRouting();
app.UseCors(MyAllowSpecificOrigins);
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers().RequireCors(MyAllowSpecificOrigins);
});
app.UseMvc();
}
In the API controller, I have a POST action for which I don't get any CORS error
[HttpPost]
[Route("[action]")]
[ActionName("thumbnail")]
public async Task<IActionResult> thumbnail([FromBody]dataDTO model)
{
.
.
.
}
In the same API, I have another POST action, for which the browser gives the above mentioned CORS error
[HttpPost]
[Route("project")]
[ActionName("project")]
public async Task<IActionResult> projectAdd([FromBody]projectDTO project)
{
.
.
.
}
I have already tried moving the app.UseCors(MyAllowSpecificOrigins); to the top of configure function in startup.cs. It did not help.
I have also tried moving the app.UseCors(MyAllowSpecificOrigins); statement and changing its order in configure with no difference.
In firefox, I have also verified that there is a Origin header in the request to the action for which we are getting CORS error.

Get request outside a controller in .netcore

Is there a way I can get the response from a method without using a controller. I mean, in order to get the tenants from the database I use attribute binding and I get it from: "http://localhost:5000/api/tenants". Is there a way I can retrieve values without using a controller, like a service? For example in angular I use httpclient to get the response. Is there anything similar in .netcore 2 webapi? Thank, you!
For Controller, it uses UseMvc middleware to route the request to controller.
If you would not use controller, you could try custom middleware to return the data directly based on the request path.
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
//your config
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
//your config
app.Map("/tenants", map => {
map.Run(async context => {
var dbContext = context.RequestServices.GetRequiredService<MVCProContext>();
var tenants = await dbContext.Users.ToListAsync();
await context.Response.WriteAsync(JsonConvert.SerializeObject(tenants));
});
});
app.Run(async context => {
await context.Response.WriteAsync($"Default response");
});
}
}

How to protect open/public Web API endpoints

I have a few Web API endpoints with no authentication/authorization since it could be used for guest users as well. These endpoints will be consumed directly through XHR/Ajax/JS. However, i would like to allow the request from only a few origins. For this, i've used the Cors middleware like below:
ConfigureServices Method
services.AddCors(options =>
{
options.AddPolicy("AllowSpecific", builder =>
builder.WithOrigins("http://localhost:55476")
.AllowAnyHeader()
.AllowAnyMethod());
});
Configure Method
app.UseCors("AllowSpecific");
This restriction works for requests coming from browsers. However, if the request is coming from Http Clients such as Postman, Fiddler, etc., the request goes through.
Is there any way to tackle such scenarios?
For lack of a better alternative for now, i've replaced CORS middleware with a custom middleware which will check each request's header Origin and allow/restrict based on configuration. This works both for cross-browser requests and HTTP Client requests.
Middleware
public class OriginRestrictionMiddleware
{
private readonly RequestDelegate _next;
private readonly IConfiguration _configuration;
private readonly ILogger _logger;
public OriginRestrictionMiddleware(RequestDelegate next, IConfiguration configuration, ILoggerFactory loggerFactory)
{
_next = next;
_configuration = configuration;
_logger = loggerFactory.CreateLogger<OriginRestrictionMiddleware>();
}
public Task Invoke(HttpContext context)
{
try
{
var allowedOriginsConfig = _configuration.GetSection("AllowedOrigins").Value;
var allowedOrigins = allowedOriginsConfig.Split(',');
_logger.LogInformation("Allowed Origins: " + allowedOriginsConfig);
var originHeader = context.Request.Headers.Where(h => h.Key == "Origin");
if (originHeader.Any())
{
var requestOrigin = originHeader.First().Value.ToString();
_logger.LogInformation("Request Origin: " + requestOrigin);
foreach (var origin in allowedOrigins)
{
//if(origin.StartsWith(requestOrigin))
if (requestOrigin.Contains(origin))
{
return _next(context);
}
}
}
context.Response.StatusCode = 401;
return context.Response.WriteAsync("Not Authorized");
}
catch(Exception ex)
{
_logger.LogInformation(ex.ToString());
throw;
}
}
}
public static class OriginRestrictionMiddlewareExtension
{
public static IApplicationBuilder UseOriginRestriction(this IApplicationBuilder builder)
{
return builder.UseMiddleware<OriginRestrictionMiddleware>();
}
}
Startup Configuration
app.UseOriginRestriction();
AppSettings.json
"AllowedOrigins": "http://localhost:55476,http://localhost:55477,chrome-extension"
chrome-extension entry is there to allow request from Postman during development. It will be removed when deployed to server.
I suspect that this solution can also be bypassed one way or another. However, i'm hoping it will work for most of the cases.