# Plan: Fix CORS Configuration **Scope**: Trivial — 3 files, ~20 lines **Goal**: Ensure server-core CORS properly accepts requests from frontend origins, configurable via `CORS_ALLOWED_ORIGINS` env var, documented in `.env.example`. --- ## Context Current CORS config in `main.go:114-130`: - Uses `CORS_ALLOWED_ORIGINS` env var (defaults to `*`) - Has BOTH `AllowOrigins` AND `AllowOriginFunc` — redundant - `strings.Contains(corsOrigins, origin)` is weak (substring match: `"example.com"` matches `"notexample.com"`) - `AllowCredentials: true` is needed for JWT `Authorization` header - `CORS_ALLOWED_ORIGINS` is NOT documented in any `.env.example` file - `SHARE_LINK_TTL` (from Phase 5.0) also missing from `.env.example` --- ## Task 1: Fix CORS config in `main.go` - [x] **File**: `apps/server-core/main.go` lines 114-130 **Replace** the current CORS block with: ```go // CORS Configuration corsOrigins := os.Getenv("CORS_ALLOWED_ORIGINS") if corsOrigins == "" { corsOrigins = "*" } corsConfig := cors.Config{ AllowMethods: []string{"GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"}, AllowHeaders: []string{"Origin", "Content-Length", "Content-Type", "Authorization", "X-Admin-Key", "X-Device-Token"}, ExposeHeaders: []string{"Content-Length"}, AllowCredentials: true, } if corsOrigins == "*" { // Wildcard: allow all origins but use AllowOriginFunc so the response // header echoes the actual origin (required when AllowCredentials=true). corsConfig.AllowOriginFunc = func(origin string) bool { return true } } else { // Explicit list: split by comma, trim spaces, exact match only. origins := strings.Split(corsOrigins, ",") for i := range origins { origins[i] = strings.TrimSpace(origins[i]) } corsConfig.AllowOrigins = origins } r.Use(cors.New(corsConfig)) ``` **Key changes**: 1. Remove simultaneous `AllowOrigins` + `AllowOriginFunc` (pick one based on `*` vs explicit) 2. When `*`: use `AllowOriginFunc` returning `true` → gin-contrib/cors echoes the requesting origin (compatible with `AllowCredentials: true`) 3. When explicit: use `AllowOrigins` with trimmed values, exact match (no more `strings.Contains` substring bug) **Verify**: `go build ./...` passes --- ## Task 2: Add `CORS_ALLOWED_ORIGINS` to root `.env.example` - [x] **File**: `.env.example` **Add** after `VITE_API_BASE_URL` line (section 5): ```env # 7. CORS Configuration # Comma-separated list of allowed origins for the API. # Use '*' to allow all origins (NOT recommended for production). # Example: https://dash.yourdomain.com,https://admin.yourdomain.com CORS_ALLOWED_ORIGINS=http://localhost:5173 # 8. Share Link Configuration # TTL for peer config share links (Go duration format) SHARE_LINK_TTL=24h ``` --- ## Task 3: Add `CORS_ALLOWED_ORIGINS` to server-core `.env.example` - [x] **File**: `apps/server-core/.env.example` **Add** at the end: ```env # CORS # Comma-separated allowed origins. '*' = allow all (not recommended for production) CORS_ALLOWED_ORIGINS=http://localhost:5173 # Share Link TTL (Go duration format) SHARE_LINK_TTL=24h ``` **Verify**: all `.env.example` files contain `CORS_ALLOWED_ORIGINS` --- ## Final Verification Wave - [x] `go build ./...` passes in `apps/server-core/` - [x] `CORS_ALLOWED_ORIGINS` present in root `.env.example` - [x] `CORS_ALLOWED_ORIGINS` present in `apps/server-core/.env.example` - [x] No other files reference `CORS_ALLOWED_ORIGINS` that need updating