Skip to content

CORS: the mandatory 'tenant' header is missing from CorsOptions.AllowedHeaders, breaking both front-ends under the restricted policy #1367

Description

@marcelo-maciel

Summary

CorsOptions.AllowedHeaders ships as [ "content-type", "authorization" ], but both React apps
send a tenant header on every request. tenant is not a CORS-safelisted request header, so
with the restricted policy (AllowAll: false) the browser's preflight rejects it and every
cross-origin call from either front-end fails before it reaches the API.

Nobody hits this in a default dev run because appsettings.Development.json overrides
CorsOptions.AllowAll to true (which implies AllowAnyHeader). It surfaces the moment someone
turns the restricted policy on — which is exactly what appsettings.json and
appsettings.Production.json ship with.

Where

  • src/Host/FSH.Starter.Api/appsettings.jsonCorsOptions.AllowedHeaders
  • src/Host/FSH.Starter.Api/appsettings.Production.json — same key
  • src/BuildingBlocks/Web/Cors/Extensions.cs — the else branch calls
    WithHeaders(settings.AllowedHeaders), so the list is the whole allow-list
  • The header itself: Finbuckle's HeaderStrategy, and both clients set it (clients/admin,
    clients/dashboard)

Reproduce

  1. Run the API with CorsOptions:AllowAll=false and CorsOptions:AllowedOrigins:0=http://localhost:5174
  2. Run clients/dashboard on 5174 and sign in
  3. Every API call fails at the preflight: Request header field tenant is not allowed by Access-Control-Allow-Headers in preflight response

Suggested fix

Add "tenant" to AllowedHeaders in both shipped appsettings. Since the header is mandatory for
this stack rather than optional, the alternative is for AddHeroCors to always append the tenant
header name to the configured list — the same shape as the ETag exposure added in the PR below,
where the framework knows the header is part of its own contract.

A Framework.Tests policy-level assertion (WithHeaders includes tenant when the restricted
branch is built) would keep it from regressing; today nothing in the suite exercises the restricted
CORS branch.

Context

Found while adding ETag/If-Match support for #1359 — same class of bug in the mirror direction
(that one was a missing WithExposedHeaders). Filing separately because it is pre-existing and
independent of that change.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions