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.