diff --git a/common/config_models.py b/common/config_models.py index fb0db10e..f4c6f64b 100644 --- a/common/config_models.py +++ b/common/config_models.py @@ -53,6 +53,18 @@ class NetworkConfig(BaseConfigModel): "Turn on this option if you are ONLY connecting from localhost." ), ) + allowed_origins: Optional[List[str]] = Field( + ["*"], + description=( + 'Origins allowed to call the API from a browser (default: ["*"]).\n' + "This is a CORS allowlist, not an auth mechanism: it only governs which\n" + "web pages a browser will let read this API's responses.\n" + 'The default "*" means any site open in your browser can send requests to\n' + "this instance, which matters most when disable_auth is on. Restrict this to\n" + 'your own frontends (e.g. ["http://localhost:8000"]) to close that off, or\n' + "use an empty list [] to block all browser (cross-origin) callers." + ), + ) disable_fetch_requests: Optional[bool] = Field( False, description=( diff --git a/config_sample.yml b/config_sample.yml index 7b16b06b..6710e658 100644 --- a/config_sample.yml +++ b/config_sample.yml @@ -20,6 +20,13 @@ network: # Turn on this option if you are ONLY connecting from localhost. disable_auth: false + # Origins allowed to call the API from a browser (default: ["*"]). + # This is a CORS allowlist, not authentication: "*" lets any site open in your + # browser send requests to this instance, which matters most when disable_auth + # is on. Restrict to your own frontends (e.g. ["http://localhost:8000"]) or use + # an empty list [] to block all browser (cross-origin) callers. + allowed_origins: ["*"] + # Disable fetching external content in response to requests,such as images from URLs. disable_fetch_requests: false diff --git a/endpoints/server.py b/endpoints/server.py index b3ed1e4a..95478881 100644 --- a/endpoints/server.py +++ b/endpoints/server.py @@ -29,11 +29,14 @@ def setup_app(host: Optional[str] = None, port: Optional[int] = None): ) app.add_exception_handler(ContextLengthHTTPException, context_length_exception_handler) - # ALlow CORS requests + # Allow CORS requests from the configured origins. + # allow_credentials stays False: TabbyAPI authenticates with a header/query + # token rather than cookies, so credentialed CORS buys nothing and would make + # Starlette reflect an arbitrary requesting origin back instead of sending "*". app.add_middleware( CORSMiddleware, - allow_origins=["*"], - allow_credentials=True, + allow_origins=config.network.allowed_origins, + allow_credentials=False, allow_methods=["*"], allow_headers=["*"], )