# Work Summary: CORS Configuration from .env **Date:** 2026-01-12 **Status:** ✅ Complete ## Overview Updated the CORS middleware to load allowed origins from the `.env` configuration file instead of being hard-coded, providing flexibility for different deployment environments. ## Problem The initial CORS implementation had two issues: 1. **Hard-coded Origins:** CORS allowed origins were hard-coded in the middleware: ```rust .allow_origin("http://localhost:3000".parse::().unwrap()) .allow_origin("http://localhost:8080".parse::().unwrap()) // etc. ``` 2. **CORS Panic:** When using `allow_credentials(true)` (required for JWT authentication), the CORS spec prohibits: - `allow_origin(Any)` - Cannot use wildcard with credentials - `allow_headers(Any)` - Cannot use wildcard headers with credentials ## Solution ### 1. Updated SecurityConfig Added separate fields for access and refresh token expiration: ```rust pub jwt_access_expiration: u64, // Default: 3600 (1 hour) pub jwt_refresh_expiration: u64, // Default: 604800 (7 days) ``` ### 2. Updated CORS Middleware Changed `create_cors_layer()` to accept origins as parameter: ```rust pub fn create_cors_layer(allowed_origins: Vec) -> CorsLayer ``` Features: - Accepts list of allowed origins from config - Falls back to default development origins if empty - Explicitly specifies headers and methods (no wildcards) - Maintains credential support for authentication ### 3. Updated AppState Added `cors_origins` field to store configuration: ```rust pub struct AppState { pub db: PgPool, pub jwt_config: Arc, pub cors_origins: Vec, // NEW } ``` ### 4. Configuration Flow ``` .env file ↓ Config::load() ↓ config.server.cors_origins ↓ AppState ↓ Server::build_router() ↓ create_cors_layer(origins) ``` ## Configuration ### .env File ```bash # Leave empty for default development origins ATTUNE__SERVER__CORS_ORIGINS= # Or specify custom origins (comma-separated) ATTUNE__SERVER__CORS_ORIGINS=http://localhost:3000,https://app.example.com,https://admin.example.com ``` ### Default Origins (when empty) If no origins are specified, the API uses these development defaults: - `http://localhost:3000` - `http://localhost:8080` - `http://127.0.0.1:3000` - `http://127.0.0.1:8080` ### Allowed Headers Explicitly set to support authentication: - `Authorization` - For Bearer tokens - `Content-Type` - For JSON requests - `Accept` - For content negotiation ### Allowed Methods - `GET` - `POST` - `PUT` - `DELETE` - `PATCH` - `OPTIONS` (for preflight requests) ## Files Modified 1. **`crates/common/src/config.rs`** - Split `jwt_expiration` into `jwt_access_expiration` and `jwt_refresh_expiration` 2. **`crates/api/src/middleware/cors.rs`** - Made CORS layer accept origins as parameter - Added default origins fallback - Fixed credentials + wildcard issue 3. **`crates/api/src/state.rs`** - Added `cors_origins` field to AppState 4. **`crates/api/src/server.rs`** - Pass `cors_origins` from state to middleware 5. **`crates/api/src/main.rs`** - Load CORS origins from config - Pass to AppState constructor - Added logging for CORS configuration 6. **`.env`** - Added `ATTUNE__SERVER__CORS_ORIGINS` configuration 7. **`.env.example`** - Updated JWT configuration comments - Added `ATTUNE__SECURITY__JWT_ACCESS_EXPIRATION` - Added `ATTUNE__SECURITY__JWT_REFRESH_EXPIRATION` 8. **`docs/quick-start.md`** - Added CORS configuration documentation ## Testing ```bash # Start server cargo run --bin attune-api # Expected logs: # INFO JWT configuration initialized (access: 3600s, refresh: 604800s) # INFO CORS configured with default development allowed origin(s) # INFO Server listening on 127.0.0.1:8080 # Test health check curl http://localhost:8080/api/v1/health # Response: {"status":"ok"} ``` ## Benefits 1. **Flexibility:** Origins can be changed without recompiling 2. **Environment-specific:** Different origins for dev/staging/prod 3. **Security:** No wildcard origins with credentials 4. **Convenience:** Defaults work for local development 5. **Documentation:** Clear configuration in .env file ## Production Deployment For production, specify exact origins: ```bash ATTUNE__SERVER__CORS_ORIGINS=https://app.example.com,https://admin.example.com ``` **Never use** wildcards or overly permissive origins in production! ## Security Notes - ✅ CORS credentials enabled (required for JWT auth) - ✅ Explicit origins (no wildcards) - ✅ Explicit headers (no wildcards) - ✅ Explicit methods - ✅ Configurable per environment ## Example Configurations ### Local Development (default) ```bash ATTUNE__SERVER__CORS_ORIGINS= ``` ### Development with Custom Frontend ```bash ATTUNE__SERVER__CORS_ORIGINS=http://localhost:3000 ``` ### Staging Environment ```bash ATTUNE__SERVER__CORS_ORIGINS=https://staging.example.com ``` ### Production (Multiple Origins) ```bash ATTUNE__SERVER__CORS_ORIGINS=https://app.example.com,https://admin.example.com,https://mobile.example.com ``` ## Next Steps - Consider adding origin validation in config loading - Add metrics for CORS rejections - Document CORS troubleshooting in API docs - Consider per-route CORS policies for future features ## References - CORS Spec: https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS - Tower HTTP CORS: https://docs.rs/tower-http/latest/tower_http/cors/ - OWASP CORS Security: https://cheatsheetseries.owasp.org/cheatsheets/CORS_Cheat_Sheet.html