[Feature] Make CORS `Access-Control-Allow-Headers` configurable via `app.conf`
[Feature] Make CORS Access-Control-Allow-Headers configurable via app.conf
Problem
Access-Control-Allow-Headers in routers/cors_filter.go:37 is hardcoded:
ctx.Output.Header(headerAllowHeaders, "Content-Type, Authorization")Any cross-origin request that includes a non-safelisted header outside Content-Type / Authorization triggers a CORS preflight that the browser rejects, because Casdoor's OPTIONS response doesn't list the header in Access-Control-Allow-Headers.
This blocks legitimate integrations and forces every team to choose between:
- Wrapping Casdoor behind a reverse proxy that rewrites the headers (the workaround in #3691).
- Patching client code to strip the headers (the workaround we ended up using for Swagger UI).
- Forking Casdoor.
Reproduction
The clearest case is Swagger UI calling the OIDC token endpoint during an authorizationCode flow:
Stack: FastAPI backend, mounted Swagger UI with OAuth2AuthorizationCodeBearer + usePkceWithAuthorizationCodeGrant: true, Casdoor as OIDC provider.
Setup:
init_data.jsonhas an application withclientId: "demo"andredirectUris: ["http://localhost:8000/api/v1/docs/oauth2-redirect"].- The FastAPI security scheme is configured from Casdoor's
.well-known/openid-configuration:authorization_endpoint: http://localhost:8001/login/oauth/authorizetoken_endpoint: http://localhost:8001/api/login/oauth/access_token
- Browser is on
http://localhost:8000/api/v1/docs.
Steps:
- Click Authorize in Swagger UI → enter
clientId→ submit. - Browser redirects to Casdoor
/login/oauth/authorize, you sign in. - Casdoor redirects back to
…/docs/oauth2-redirect?code=…. - Swagger UI tries to
POST http://localhost:8001/api/login/oauth/access_tokenwith PKCEcode_verifier.
Expected: Token returned, Swagger UI saves Bearer token.
Actual:
Browser console:
Access to fetch at 'http://localhost:8001/api/login/oauth/access_token' from origin
'http://localhost:8000' has been blocked by CORS policy: Request header field
x-requested-with is not allowed by Access-Control-Allow-Headers in preflight response.The OPTIONS preflight succeeds with HTTP 200 (verified via curl), Casdoor returns:
Access-Control-Allow-Origin: http://localhost:8000
Access-Control-Allow-Headers: Content-Type, Authorization
Access-Control-Allow-Credentials: trueX-Requested-With: XMLHttpRequest is added by Swagger UI's bundled fetch client (a long-standing legacy behavior that cannot be disabled from configuration). The browser blocks the actual POST because X-Requested-With isn't in the allow-list.
Why this is broader than Swagger UI
Many HTTP clients send X-Requested-With automatically:
- Swagger UI 3.x and 5.x (bundled inside FastAPI, Drf-Spectacular, Spring Boot's Swagger integration, etc.).
- jQuery
$.ajax. - Axios with certain interceptors.
- Older
XMLHttpRequest-based code.
Other common non-safelisted headers that integrators want to add:
X-CSRF-Token(when fronting Casdoor with apps that use CSRF tokens).X-Trace-Id/X-Request-Id(observability stacks).If-Modified-Since,Cache-Control(caching layers — see the nginx workaround in #3691).
Today every integrator has to either run Casdoor behind a reverse proxy or patch their client. A one-line config would solve it for everyone.
Proposed solution
Add an optional corsAllowHeaders key to app.conf with the current value as default. Read it in setCorsHeaders.
conf/app.conf — add:
# Comma-separated list of headers added to Access-Control-Allow-Headers.
# Default: "Content-Type, Authorization"
corsAllowHeaders = "Content-Type, Authorization"routers/cors_filter.go — change setCorsHeaders:
func setCorsHeaders(ctx *context.Context, origin string) {
ctx.Output.Header(headerAllowOrigin, origin)
ctx.Output.Header(headerAllowMethods, "POST, GET, OPTIONS, DELETE")
- ctx.Output.Header(headerAllowHeaders, "Content-Type, Authorization")
+ allowHeaders := conf.GetConfigString("corsAllowHeaders")
+ if allowHeaders == "" {
+ allowHeaders = "Content-Type, Authorization"
+ }
+ ctx.Output.Header(headerAllowHeaders, allowHeaders)
ctx.Output.Header(headerAllowCredentials, "true")
if ctx.Input.Method() == "OPTIONS" {
ctx.ResponseWriter.WriteHeader(http.StatusOK)
}
}That preserves backward compatibility (existing deployments behave exactly as before) and lets operators opt in to whatever set of headers their stack needs.
Same idea, optionally bigger
If the maintainers prefer a richer config surface, the same pattern could expose:
corsAllowMethods(also hardcoded toPOST, GET, OPTIONS, DELETE).corsAllowCredentials(truehardcoded).corsExposeHeaders(currently not sent at all).
Happy to send a PR for the minimal corsAllowHeaders change first if the direction is acceptable.
Related
- #3691 — nginx-level workaround that adds
X-Requested-Withand others to the allow-list. - #1408 — earlier CORS report; closed without addressing allow-headers.
- swagger-api/swagger-ui#6081 — long-standing Swagger UI CORS issue, downstream of every OAuth provider that doesn't accept
X-Requested-With.
Environment
- Casdoor
latestDocker image (verified against currentmasterrouters/cors_filter.go). - Browsers: Chrome 134+, Firefox 124+, Safari 17+ (all enforce preflight strictly per CORS spec).
- Use case: any OIDC integration whose client library adds non-safelisted headers — confirmed with FastAPI's bundled Swagger UI but applies to any HTTP client emitting
X-Requested-Withor similar.
Source: casdoor/casdoor