aspnetcore icon indicating copy to clipboard operation
aspnetcore copied to clipboard

CORS: Headers are not set after client has been redirected from http to https

Open wavedeck opened this issue 5 months ago • 0 comments

Is there an existing issue for this?

  • [X] I have searched the existing issues

Describe the bug

I've made an oversight by doing a GET request via http:// instead of https:// and wasn't able to use CORS even after being redirected automatically. It however works fine when i correctly use the https:// scheme in my fetch call to begin with.

The application is set-up behind a Caddy reverse proxy, that automatically redirects insecure http requests to https. (resulting in a 307 - Internal Redirect from javascript's fetch function)

Unfortunately, ASP.NET does not return any CORS headers after being redirected (which might also be good in terms of security - but confusing for the developer)

A section in the official docs mentions that CORS requests to the insecure http scheme will fail with ERR_INVALID_REDIRECT when using app.UseHttpsRedirection(), but since i don't use this feature, as this is the sole responsibility of my reverse proxy, this is not what happens at all.

Instead, my browser (chrome dev) throws an error like:

Access to fetch at 'https://api.example.dev/api/v1/product-categories/' 
(redirected from 'http://api.example.dev/api/v1/product-categories/')
from origin 'http://localhost:8000' has been blocked by CORS policy: 
No 'Access-Control-Allow-Origin' header is present on the requested resource.

Expected Behavior

I expected that ASP.NET Core allowed http to https redirects for the same target host / origin and keeps CORS functional. This allows the client to follow redirects and still use CORS.

Steps To Reproduce

Client Side (running localhost:8000 with python3 -m http.server):

fetch('http://glas-online.invadox.dev/api/v1/product-categories/')
  .then(response => response.json())
  .then(data => console.log(data))
  .catch(error => console.log('Error:', error));

Server Side: (webapi template)

// Program.cs
var builder = WebApplication.CreateBuilder(args);

builder.Services.AddScoped<IProductCategoriesRepository, ProductCategoriesRepository>();
builder.Services.AddControllers();

builder.Services.AddCors(opts =>
{
    opts.AddDefaultPolicy(policy =>
    {
        var allowedHosts = builder.Configuration["Cors:AllowedOrigins"];
        
        if (!string.IsNullOrWhiteSpace(allowedHosts))
        {
            policy.WithOrigins(allowedHosts.Split(","));
        }
        else
        {
            policy.AllowAnyOrigin();
            Console.WriteLine("[Warn] Cors:AllowedOrigins not set in appsettings.json, allowing any origin");
        }
        
        policy.AllowAnyMethod();
        policy.AllowAnyHeader();
    });
});

var app = builder.Build();

app.UseCors();
app.UseAuthorization();
app.MapControllers();

app.Run();

Server Side: appsettings.json

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "AllowedHosts": "*",
  "Cors": {
    "AllowedOrigins": "http://localhost:3000,http://localhost:8000"
  }
}

Server Side: Caddy Config

api.example.dev {
    reverse_proxy 127.0.0.1:5000
}

Exceptions (if any)

No response

.NET Version

8.0.200

Anything else?

No response

wavedeck avatar Mar 12 '24 18:03 wavedeck