3

There are millions of articles and questions related to this issue and yet I cannot find what's wrong with my code. I have Startup, StartupProduction, and StartupDevelopment as the following. Also, I am using ASP.Net Core 5, and based on the documentation I think I am doing this correctly.

FYI, at first, I used AllowAnyOrigin for development but I also test .WithOrigins("http://localhost:3000") and it works fine. My backend runs under https://localhost:44353 in development and under https://api.example.com in production.

public class Startup
{
    protected const string CorsPolicyName = "CorsPolicyName";

    public virtual void ConfigureServices(IServiceCollection services)
    {
        services.AddControllers()
            .AddJsonOptions(options =>
            {
                options.JsonSerializerOptions.Converters.Add(
                    new System.Text.Json.Serialization.JsonStringEnumConverter());
            });

        services.AddABunchOfOtherServices();
    }

    public virtual void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        app.UseHttpsRedirection();
        app.UseStaticFiles();
        app.UseRouting();
        app.UseCors(CorsPolicyName);
        app.UseAuthentication();
        app.UseAuthorization();

        app.UseMiddleware<CheckUserConfirmedMiddleware>();

        app.UseEndpoints(endpoints =>
        {
            endpoints.MapControllerRoute
            (
                name: "default",
                pattern: "{controller=Home}/{action=Index}/{id?}"
            )
            .RequireCors(CorsPolicyName);
        });
    }
}

public class StartupProduction : Startup
{
    public override void ConfigureServices(IServiceCollection services)
    {
        services.AddCors(options =>
        {
            options.AddPolicy(
                CorsPolicyName,
                policy => policy
                    .WithOrigins("https://example.com", "http://example.com")
                    //.WithOrigins(Configuration.GetValue<string>("AllowedHosts").Split(';').ToArray())
                    .AllowAnyMethod()
                    .AllowAnyHeader());
        });

        base.ConfigureServices(services);
    }

    public override void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        app.UseMiddleware(typeof(ErrorHandlingMiddleware));

        // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
        app.UseHsts();

        base.Configure(app, env);
    }
}

public class StartupDevelopment : Startup
{
    public override void ConfigureServices(IServiceCollection services)
    {
        services.AddCors(options =>
            options.AddPolicy(
                CorsPolicyName,
                policy =>
                    policy
                        //.AllowAnyOrigin()
                        .WithOrigins("http://localhost:3000")
                        .AllowAnyMethod()
                        .AllowAnyHeader()
            )
        );

        base.ConfigureServices(services);

        services.AddSwaggerGen(....);
    }

    public override void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        app.UseMiddleware<DevelopmentErrorHandlingMiddleware>();

        base.Configure(app, env);

        app.UseSwagger();

        app.UseSwaggerUI(options =>
        {
            options.SwaggerEndpoint("swagger/v1/swagger.json", "API v1");
            options.RoutePrefix = string.Empty;
        });
    }
}

I also tried the default policy.

Update

I have set the Environment to Production in the visual studio to debug it and now I am facing the same issue in development.

Access to fetch at 'https://localhost:44353/api/v1/User' from origin 'http://localhost:3000' 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.

Workaround solution

I notice that it is the IIS blocking the request. It only works when I have "AllowedHosts": "*", in my appsettings.json. So, as a workaround, I have added "MyRandomKey": "https://example.com", in my appsettings.json and use the following in my Startup.

services.AddCors(options =>
                options.AddPolicy(
                    CorsPolicyName,
                    policy =>
                        policy
                            .WithOrigins(Configuration.GetValue<string>("MyRandomKey").Split(";").ToArray())
                            .AllowAnyMethod()
                            .AllowAnyHeader()
                )
            );
Hamid Mayeli
  • 805
  • 1
  • 14
  • 24

3 Answers3

3

AllowedHosts & CORS are different.

AllowedHosts is for host filtering so even if you have configured CORS Policy in your application but didn't allowed host then IIS will reject the request.

Please refer this link: Difference between AllowedHosts in appsettings.json and UseCors in .NET Core API 3.x

And by default it's * but you can change it to according to your requirements. In your case you can set "api.example.com" or if you want to allow also from localhost then "api.example.com;localhost". Once you set it then IIS will start accepting requests from those domains.

Once IIS will start accepting request then your application level configured CORS policy will be applied and work. So basically the CORS is to allow access of resources in your WebAPI.

Subhash
  • 31
  • 4
  • Thank you @WebDev512. You are absolutely right but the IIS was not accepting the request from the domain listed in the `AllowedHosts`. I mentioned this in the *Workaround* section. The issue probebly was related to the installed `hosting bundle` on my machine. – Hamid Mayeli May 12 '21 at 07:11
  • Oh, Ok. I hoped that you already have installed Hosting Bundler according to .Net Core runtime version. Anyway, Is it working now? – Subhash May 15 '21 at 11:20
1

From this doc about CORS preflight request, you can find following information:

A CORS preflight request is used to determine whether the resource being requested is set to be shared across origins by the server. And The OPTIONS requests are always anonymous, server would not correctly respond to the preflight request if anonymous authentification is not enabled.

Access to fetch at 'https://localhost:44353/api/v1/User' from origin 'http://localhost:3000' 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.

To fix the above issue, if you run the App(s) on local for testing purpose with CORS, you can try to enable anonymous authentification.

Besides, if your App(s) are hosted on IIS, you can try to install IIS CORS module and configure CORS for the app.

Zhi Lv
  • 18,845
  • 1
  • 19
  • 30
  • The user API I mentioned here is just the sample, my other actions that do not need the authentication, have the same result. But I need to check the IIS CORS module. – Hamid Mayeli Jan 04 '21 at 08:56
0

I think it's Ok, and also, you can get origins from DB or JSON files. also, you can use ActionFilterAttribute and this part of the code

    var csp = "default-src 'self' http://localhost:3000; object-src 'none'; frame-ancestors 'none'; sandbox allow-forms allow-same-origin allow-scripts; base-uri 'self';";

if (!context.HttpContext.Response.Headers.ContainsKey("Content-Security-Policy"))
{
    context.HttpContext.Response.Headers.Add("Content-Security-Policy", csp);
}

if (!context.HttpContext.Response.Headers.ContainsKey("X-Content-Security-Policy"))
{
    context.HttpContext.Response.Headers.Add("X-Content-Security-Policy", csp);
}
hmd.nikoo
  • 16
  • 1
  • Thank you @hmd.nikoo, it seems ok but it only works for development, not production. – Hamid Mayeli Jan 03 '21 at 11:42
  • If I'm not mistaken, you want to change the URL in the product mode. You can use appsettings.json and appsettings.Development.json to switch between them. By using ActionFilterAttribute, you have the chance to manage security risks and use the policy for specific actions. – hmd.nikoo Jan 03 '21 at 14:28