CORS Configuration
FlowPilot services support Cross-Origin Resource Sharing (CORS) to enable web applications running in browsers to access the APIs.
Overview
CORS is required when your web application's frontend (e.g., running on https://app.example.com) needs to make requests to FlowPilot APIs (running on different domains like https://flowpilot-domain-services-api-*.run.app).
All five FlowPilot services include CORS middleware: - authz-api (port 8002 local, GCP Cloud Run) - domain-services-api (port 8003 local, GCP Cloud Run) - delegation-api (port 8005 local, GCP Cloud Run) - persona-api (port 8006 local, GCP Cloud Run) - ai-agent-api (port 8004 local, GCP Cloud Run)
Configuration
CORS behavior is controlled via environment variables in the shared security.py library:
Environment Variables
| Variable | Description | Default | Example |
|---|---|---|---|
CORS_ALLOWED_ORIGINS |
Comma-separated list of allowed origins, or * for all |
* |
https://app.example.com,https://admin.example.com |
CORS_ALLOW_CREDENTIALS |
Allow credentials (cookies, auth headers) | true |
true or false |
CORS_ALLOW_METHODS |
Allowed HTTP methods, or * for all |
* |
GET,POST,PUT,DELETE |
CORS_ALLOW_HEADERS |
Allowed headers, or * for all |
* |
Authorization,Content-Type |
Local Development (Docker Compose)
All services are configured with permissive CORS settings in docker-compose.yml:
environment:
- CORS_ALLOWED_ORIGINS=*
- CORS_ALLOW_CREDENTIALS=true
- CORS_ALLOW_METHODS=*
- CORS_ALLOW_HEADERS=*
This allows any web application running on localhost (e.g., http://localhost:3000, http://localhost:5173) to access the APIs during development.
Production (GCP Cloud Run)
CORS settings for production are defined in cloud-run-envs/*.yaml files:
cloud-run-envs/authz-api.yamlcloud-run-envs/domain-services-api.yamlcloud-run-envs/delegation-api.yamlcloud-run-envs/persona-api.yamlcloud-run-envs/ai-agent-api.yaml
Default Production Settings
Currently configured with permissive settings for development/testing:
CORS_ALLOWED_ORIGINS: "*"
CORS_ALLOW_CREDENTIALS: "true"
CORS_ALLOW_METHODS: "*"
CORS_ALLOW_HEADERS: "*"
Recommended Production Settings
For production deployments, restrict origins to your actual web application domains:
# Example: Production CORS configuration
CORS_ALLOWED_ORIGINS: "https://app.example.com,https://admin.example.com"
CORS_ALLOW_CREDENTIALS: "true"
CORS_ALLOW_METHODS: "GET,POST,PUT,DELETE"
CORS_ALLOW_HEADERS: "Authorization,Content-Type,X-Request-ID"
Security Considerations
Development vs. Production
Development:
- Using CORS_ALLOWED_ORIGINS=* is acceptable for local development
- Simplifies testing with various frontend frameworks and ports
Production:
- Never use * in production - this allows any website to access your APIs
- Specify exact origins (protocol + domain + port if non-standard)
- Use HTTPS origins only (except localhost for development)
Credentials and Wildcard Origins
Important: When CORS_ALLOW_CREDENTIALS=true, you cannot use CORS_ALLOWED_ORIGINS=* according to the CORS specification. Browsers will reject such requests.
If you need credentials (JWT tokens in Authorization header), you must specify exact origins:
# ✅ Correct: specific origins with credentials
CORS_ALLOWED_ORIGINS: "https://app.example.com"
CORS_ALLOW_CREDENTIALS: "true"
# ❌ Incorrect: wildcard with credentials (browsers reject)
CORS_ALLOWED_ORIGINS: "*"
CORS_ALLOW_CREDENTIALS: "true"
Best Practices
- Restrict Origins: Only allow your actual frontend application domains
- Use HTTPS: Production origins should use HTTPS (except localhost for dev)
- Limit Methods: Only allow HTTP methods your API actually uses
- Limit Headers: Only allow headers your API requires
- Monitor: Log CORS errors to detect misconfiguration or unauthorized access attempts
Updating CORS Settings
Local Development
- Edit
docker-compose.ymlfor the relevant service(s) - Rebuild and restart:
GCP Cloud Run
- Edit the relevant
cloud-run-envs/*.yamlfile - Rebuild the service (triggers image rebuild with new shared library):
- Redeploy with updated environment variables:
Testing CORS
Browser DevTools
- Open your web application in a browser
- Open DevTools → Network tab
- Make a request to a FlowPilot API
- Check the response headers for:
CORS Preflight Requests
For requests with custom headers (like Authorization), browsers send a preflight OPTIONS request:
# Example preflight request
curl -X OPTIONS http://localhost:8003/v1/workflows \
-H "Origin: http://localhost:3000" \
-H "Access-Control-Request-Method: POST" \
-H "Access-Control-Request-Headers: authorization,content-type" \
-v
You should see CORS headers in the response.
Common CORS Errors
Error: "Access to fetch at '...' from origin '...' has been blocked by CORS policy"
Causes:
1. Origin not in CORS_ALLOWED_ORIGINS
2. Using * with CORS_ALLOW_CREDENTIALS=true
3. Missing required header in CORS_ALLOW_HEADERS
4. Service not configured with CORS middleware (should not happen in FlowPilot)
Solution: - Check environment variables in running service - Verify origin matches exactly (including protocol and port) - Check browser DevTools → Console for specific CORS error details
Implementation Details
CORS is implemented in flowpilot-services/shared-libraries/security.py:
def get_cors_config() -> dict[str, Any]:
"""
Get CORS configuration from environment variables.
Returns dict suitable for passing to CORSMiddleware constructor.
"""
origins_str = os.environ.get("CORS_ALLOWED_ORIGINS", "*")
origins = [o.strip() for o in origins_str.split(",")] if origins_str != "*" else ["*"]
allow_credentials = os.environ.get("CORS_ALLOW_CREDENTIALS", "true").lower() == "true"
methods_str = os.environ.get("CORS_ALLOW_METHODS", "*")
methods = [m.strip() for m in methods_str.split(",")] if methods_str != "*" else ["*"]
headers_str = os.environ.get("CORS_ALLOW_HEADERS", "*")
headers = [h.strip() for h in headers_str.split(",")] if headers_str != "*" else ["*"]
return {
"allow_origins": origins,
"allow_credentials": allow_credentials,
"allow_methods": methods,
"allow_headers": headers,
}
Each service applies CORS middleware in its FastAPI app initialization:
from fastapi.middleware.cors import CORSMiddleware
import security
# Add CORS middleware
cors_config = security.get_cors_config()
app.add_middleware(
CORSMiddleware,
allow_origins=cors_config["allow_origins"],
allow_credentials=cors_config["allow_credentials"],
allow_methods=cors_config["allow_methods"],
allow_headers=cors_config["allow_headers"],
)
Related Documentation
- Integration Guide - Web application integration examples
- Security Architecture - Overall security model
- Deployment Guide - GCP Cloud Run deployment with CORS