291 lines
7.1 KiB
Markdown
291 lines
7.1 KiB
Markdown
# 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 |