CORS controls whether browser code can read a cross-origin response. It does not decide whether the caller is allowed to use the API.

The distinction matters because CORS is enforced by browsers. A console application, HttpClient, curl, or another server can call the same endpoint without participating in the browser’s CORS checks.

Configure the browser boundary and the authorization boundary separately:

builder.Services.AddCors(options =>
{
    options.AddPolicy("Frontend", policy =>
        policy.WithOrigins("https://app.example.com")
              .WithHeaders("Authorization")
              .WithMethods("GET"));
});

// Authentication scheme and handler registration are omitted.
builder.Services.AddAuthorization(options =>
{
    options.AddPolicy("CanReadOrders", policy =>
        policy.RequireClaim("permission", "orders.read"));
});

app.UseCors("Frontend");
app.UseAuthentication();
app.UseAuthorization();

app.MapGet("/orders/{id}", GetOrderAsync)
   .RequireAuthorization("CanReadOrders");

The CORS policy allows browser code from the configured origin to access the response. Because this request uses the Authorization header, the browser sends a preflight first, and the policy determines whether it sends the actual request. RequireAuthorization still decides whether the authenticated caller may read the order.

More generally, a CORS failure is not proof that the API is protected. For a simple request, the server may still execute the operation while the browser withholds the response from JavaScript. For a preflighted request, a failed preflight prevents the browser from sending the actual request. In either case, a non-browser client can call the endpoint directly. Conversely, a valid access token does not allow browser code to access a cross-origin response when the required CORS headers are missing.

A preflight is an OPTIONS request that checks the permitted origin, method, and headers. It concerns browser access, not the business permission for the later operation.

Keep CSRF separate as well: CORS is not a substitute for CSRF protection when the browser attaches credentials automatically, such as authentication cookies.

Keep both boundaries explicit:

  • CORS controls browser-script access to cross-origin responses and, when preflight is required, whether the browser sends the actual request.
  • Authentication establishes who the caller is.
  • Authorization decides what that caller may do.

If the API must be protected, enforce that protection in application or endpoint authorization. CORS is not a substitute.