CORS when calling API in .net core mvc

Below is the code that I use to do protected ASP.NET Core MVC login and protected api using the same project (for some reason). The problem is the API only has intended result when the user logged in. If getting them with the test token through curl in swagger UI, I see CORS error. For your reference, the razor views and api do only show results when the user logs in, and the useful codes are quoted here:

    builder.Services.Configure<CookiePolicyOptions>(options =>

    {

        options.CheckConsentNeeded = context => true;

        options.MinimumSameSitePolicy = SameSiteMode.None;

    });


builder.Services.AddAuth0WebAppAuthentication(options =>

    {

      options.Domain = builder.Configuration\["Auth0:Domain"\];

      options.ClientId = builder.Configuration\["Auth0:ClientId"\];

      options.ClientSecret = builder.Configuration\["Auth0:ClientSecret"\];

    })

    .WithAccessToken(options =>

    {

      options.Audience = builder.Configuration\["Auth0:Audience"\]; 

    });

    builder.Services.AddAuth0ApiAuthentication("Auth",



        Configuration.GetSection("Auth0"),

        configureJwtBearer: jwt =>

        {

            jwt.TokenValidationParameters = new TokenValidationParameters

            {

                ValidAudiences = new\[\]

                {

                    Configuration\["Auth0:Audience"\]

                }

            };

            jwt.Events = new JwtBearerEvents

            {

                OnMessageReceived = context =>

                {

                    var token = context.Request.Query\["access_token"\].FirstOrDefault();

                    if (string.IsNullOrEmpty(token))

                    {

                        token = context.Request.Headers\["X-API-Token"\].FirstOrDefault();

                    }

                    

                    if (!string.IsNullOrEmpty(token))

                    {

                        context.Token = token;

                    }

                    return Task.CompletedTask;

                }

            };

        }

    );

Hi @planyway

  1. The Scheme Conflict: When you use both AddAuth0WebAppAuthentication (cookie-based) and AddAuth0ApiAuthentication (JWT Bearer-based) in the same project without explicitly telling your API controllers which scheme to use, ASP.NET defaults to the cookie scheme. Curl/Swagger requests using Bearer tokens are ignored or rejected.
  2. The CORS Illusion: The CORS error you see in Swagger is often a “red herring.” When an API request fails authentication (401 Unauthorized), ASP.NET Core often short-circuits the pipeline before the CORS headers are appended to the response, causing the browser (or Swagger UI) to report a CORS error instead of an Auth error.

To fix this, we need to explicitly configure your ASP.NET Core pipeline so that the MVC pages use cookies, the APIs accept JWT Bearer tokens, and the CORS middleware runs in the correct order.

Step 1: Explicitly tell your API controllers to accept JWTs
Right now, your API controllers are likely just using [Authorize]. You must update them to explicitly look for the JWT Bearer scheme that AddAuth0ApiAuthentication registered.

Update your API controllers:

using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;

[ApiController]
[Route("api/[controller]")]
// Force this controller to ONLY look at Bearer tokens (not cookies)
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)] 
public class MyProtectedApiController : ControllerBase
{
    // ...
}

Step 2: Fix the Middleware Order (Program.cs)
The order of app.Use... statements in ASP.NET Core is critical. UseCors must come before UseAuthentication and UseAuthorization so that CORS headers are appended even on 401 responses.

var app = builder.Build();

app.UseCors("MyCorsPolicy"); 

app.UseRouting();

app.UseAuthentication();
app.UseAuthorization();

app.UseSwagger();
app.UseSwaggerUI();

app.MapControllers();
app.MapDefaultControllerRoute();

app.Run();

Step 3: Ensure CORS allows Swagger
Make sure your CORS policy (defined in builder.Services.AddCors) explicitly allows the origin where Swagger is running (e.g., https://localhost:5001), or uses AllowAnyOrigin() for local development testing.

If you have any other questions, let me know!

Kind Regards,
Nik