# CORS Troubleshooting Guide This guide explains how CORS is configured in Attune and how to troubleshoot common issues. ## Architecture Overview Attune uses a **proxy-based architecture** for local development: ``` Browser (localhost:3000) → Vite Dev Server → Proxy → API Server (localhost:8080) ``` ### Why Use a Proxy? 1. **Avoids CORS issues** - Requests appear to come from the same origin 2. **Simpler configuration** - No need to configure CORS for every local development scenario 3. **Production-ready** - In production, use a reverse proxy (nginx/caddy) the same way ## Current Configuration ### Frontend (Vite Proxy) **File:** `web/vite.config.ts` ```typescript server: { port: 3000, proxy: { "/api": { target: "http://localhost:8080", changeOrigin: true, }, "/auth": { target: "http://localhost:8080", changeOrigin: true, }, }, } ``` **What this does:** - Requests to `http://localhost:3000/api/*` → `http://localhost:8080/api/*` - Requests to `http://localhost:3000/auth/*` → `http://localhost:8080/auth/*` ### Frontend API Client **File:** `web/src/lib/api-config.ts` ```typescript const API_BASE_URL = import.meta.env.VITE_API_BASE_URL || ""; OpenAPI.BASE = API_BASE_URL; OpenAPI.WITH_CREDENTIALS = true; OpenAPI.CREDENTIALS = "include"; ``` **Key settings:** - `BASE = ""` - Makes requests relative (uses proxy) - `WITH_CREDENTIALS = true` - Sends cookies/auth headers - `CREDENTIALS = "include"` - Include credentials in cross-origin requests ### Backend CORS Configuration **File:** `crates/api/src/middleware/cors.rs` **Default allowed origins** (when no custom origins configured): ``` http://localhost:3000 http://localhost:5173 http://localhost:8080 http://127.0.0.1:3000 http://127.0.0.1:5173 http://127.0.0.1:8080 ``` **Allowed methods:** GET, POST, PUT, DELETE, PATCH, OPTIONS **Allowed headers:** Authorization, Content-Type, Accept **Credentials:** Enabled (`allow_credentials(true)`) ## Common Issues & Solutions ### Issue 1: "CORS policy: No 'Access-Control-Allow-Origin' header" **Cause:** Frontend making direct requests to `http://localhost:8080` instead of using proxy **Solution:** ```typescript // ❌ Wrong - bypasses proxy const response = await fetch('http://localhost:8080/auth/login', ...); // ✅ Correct - uses proxy const response = await fetch('/auth/login', ...); ``` **Check:** 1. Verify `OpenAPI.BASE = ""` in `web/src/lib/api-config.ts` 2. Don't set `VITE_API_BASE_URL` environment variable locally ### Issue 2: "CORS policy: credentials mode is 'include'" **Cause:** `WITH_CREDENTIALS` mismatch between client and server **Solution:** 1. Ensure `OpenAPI.WITH_CREDENTIALS = true` (frontend) 2. Ensure CORS layer has `.allow_credentials(true)` (backend) 3. Cannot use `allow_origin(Any)` with credentials - must specify origins ### Issue 3: OPTIONS preflight request failing **Cause:** Browser sends OPTIONS request before actual request, server doesn't handle it **Solution:** - Axum's `CorsLayer` automatically handles OPTIONS requests - Verify `Method::OPTIONS` is in `.allow_methods()` - Check server logs for OPTIONS requests ### Issue 4: Custom frontend port not working **Cause:** Your dev server runs on a different port (e.g., 5173 instead of 3000) **Solution:** **Option A:** Update Vite config to use port 3000: ```typescript server: { port: 3000, // ... } ``` **Option B:** Add your port to backend CORS origins: ```yaml # config.development.yaml server: cors_origins: - "http://localhost:5173" - "http://localhost:3000" ``` Or set environment variable: ```bash export ATTUNE__SERVER__CORS_ORIGINS='["http://localhost:5173"]' ``` ### Issue 5: Production deployment CORS errors **Cause:** Production frontend domain not in allowed origins **Solution:** 1. **Set production CORS origins:** ```yaml # config.production.yaml server: cors_origins: - "https://app.example.com" - "https://www.example.com" ``` 2. **Use environment variables:** ```bash export ATTUNE__SERVER__CORS_ORIGINS='["https://app.example.com"]' ``` 3. **Or use a reverse proxy** (recommended): - Frontend and backend served from same domain - No CORS needed! ## Testing CORS Configuration ### Test 1: Verify Proxy is Working ```bash # Start dev server cd web npm run dev # In another terminal, test proxy curl -v http://localhost:3000/auth/login ``` Should show request proxied to backend. ### Test 2: Check Allowed Origins ```bash # Test CORS preflight curl -X OPTIONS http://localhost:8080/auth/login \ -H "Origin: http://localhost:3000" \ -H "Access-Control-Request-Method: POST" \ -v ``` Look for these headers in response: ``` Access-Control-Allow-Origin: http://localhost:3000 Access-Control-Allow-Credentials: true Access-Control-Allow-Methods: GET, POST, PUT, DELETE, PATCH, OPTIONS ``` ### Test 3: Actual Login Request ```bash curl -X POST http://localhost:8080/auth/login \ -H "Origin: http://localhost:3000" \ -H "Content-Type: application/json" \ -d '{"login":"admin","password":"admin"}' \ -v ``` Check for `Access-Control-Allow-Origin` in response. ## Development Workflow ### Recommended Setup 1. **Start backend services:** ```bash ./scripts/start_services_test.sh ``` 2. **Start frontend dev server:** ```bash cd web npm run dev ``` 3. **Access UI:** - Open browser to `http://localhost:3000` - All API requests automatically proxied - No CORS issues! ### Alternative: Direct API Access If you need to access API directly (e.g., testing with curl/Postman): 1. Set environment variable: ```bash export ATTUNE__SERVER__CORS_ORIGINS='["http://localhost:3000","*"]' ``` 2. Restart API service **⚠️ Warning:** Never use `"*"` in production! ## Environment Variables Reference ```bash # Allow all origins (DEVELOPMENT ONLY!) export ATTUNE__SERVER__CORS_ORIGINS='["*"]' # Allow specific origins export ATTUNE__SERVER__CORS_ORIGINS='["http://localhost:3000","http://localhost:5173"]' # Frontend: Use direct API access (bypasses proxy) export VITE_API_BASE_URL='http://localhost:8080' # Frontend: Use proxy (default, recommended) # Don't set VITE_API_BASE_URL, or set to empty string export VITE_API_BASE_URL='' ``` ## Quick Fix Checklist If you're getting CORS errors, check these in order: - [ ] Frontend dev server running on `localhost:3000`? - [ ] `OpenAPI.BASE = ""` in `web/src/lib/api-config.ts`? - [ ] Vite proxy configured for `/api` and `/auth`? - [ ] Backend API running on `localhost:8080`? - [ ] Not setting `VITE_API_BASE_URL` environment variable? - [ ] Browser console shows requests to `localhost:3000`, not `8080`? ## Additional Resources - [MDN: CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) - [Vite Proxy Config](https://vitejs.dev/config/server-options.html#server-proxy) - [Tower HTTP CORS](https://docs.rs/tower-http/latest/tower_http/cors/) ## Summary **For local development:** - Use Vite proxy (default configuration) - Set `OpenAPI.BASE = ""` - Frontend requests go to `localhost:3000`, proxied to `8080` **For production:** - Use reverse proxy (nginx/caddy/traefik) - OR configure explicit CORS origins in `config.production.yaml` - Never use wildcard `*` origins with credentials