This is an automated email from the ASF dual-hosted git repository.
vatsrahul1001 pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/airflow.git
The following commit(s) were added to refs/heads/main by this push:
new 2723f66d6ed Make CORS allow_credentials configurable (#66503)
2723f66d6ed is described below
commit 2723f66d6ed086eb68fddcdc0798e1ae3df40904
Author: Jarek Potiuk <[email protected]>
AuthorDate: Tue May 19 15:37:55 2026 +0200
Make CORS allow_credentials configurable (#66503)
* Make CORS allow_credentials configurable
The FastAPI CORS middleware was initialized with allow_credentials=True
hardcoded. Add a new [api] access_control_allow_credentials boolean
option (default True to preserve existing behavior) so deployments that
configure access_control_allow_origins but do not want credentialed
cross-origin requests can opt out.
* Address review nits: bump version_added to 3.2.2, hoist imports
- config.yml: version_added 3.2.0 -> 3.2.2 to match the backport target
- test_app.py: move FastAPI, CORSMiddleware, init_config, and conf_vars
imports to the top of the file instead of inline inside the test method.
---------
Co-authored-by: vatsrahul1001 <[email protected]>
---
airflow-core/docs/security/api.rst | 5 +++++
.../src/airflow/api_fastapi/core_api/app.py | 3 ++-
.../src/airflow/config_templates/config.yml | 10 ++++++++++
.../tests/unit/api_fastapi/core_api/test_app.py | 23 ++++++++++++++++++++++
4 files changed, 40 insertions(+), 1 deletion(-)
diff --git a/airflow-core/docs/security/api.rst
b/airflow-core/docs/security/api.rst
index 811e51084d6..c02033d0b12 100644
--- a/airflow-core/docs/security/api.rst
+++ b/airflow-core/docs/security/api.rst
@@ -86,6 +86,11 @@ from scripts running in the browser.
access_control_allow_methods = POST, GET, OPTIONS, DELETE
access_control_allow_origins = https://exampleclientapp1.com
https://exampleclientapp2.com
+The ``Access-Control-Allow-Credentials`` header is included by default. Set
+``access_control_allow_credentials = False`` if you have configured
+``access_control_allow_origins`` and do not want browsers to send credentials
+(cookies, ``Authorization`` header) with cross-origin requests.
+
Page size limit
---------------
diff --git a/airflow-core/src/airflow/api_fastapi/core_api/app.py
b/airflow-core/src/airflow/api_fastapi/core_api/app.py
index c36bb6c978f..20ecbedb236 100644
--- a/airflow-core/src/airflow/api_fastapi/core_api/app.py
+++ b/airflow-core/src/airflow/api_fastapi/core_api/app.py
@@ -143,12 +143,13 @@ def init_config(app: FastAPI) -> None:
allow_origins = conf.getlist("api", "access_control_allow_origins")
allow_methods = conf.getlist("api", "access_control_allow_methods")
allow_headers = conf.getlist("api", "access_control_allow_headers")
+ allow_credentials = conf.getboolean("api",
"access_control_allow_credentials", fallback=True)
if allow_origins or allow_methods or allow_headers:
app.add_middleware(
CORSMiddleware,
allow_origins=allow_origins,
- allow_credentials=True,
+ allow_credentials=allow_credentials,
allow_methods=allow_methods,
allow_headers=allow_headers,
)
diff --git a/airflow-core/src/airflow/config_templates/config.yml
b/airflow-core/src/airflow/config_templates/config.yml
index 57ddd14a6cb..f93642dd8ed 100644
--- a/airflow-core/src/airflow/config_templates/config.yml
+++ b/airflow-core/src/airflow/config_templates/config.yml
@@ -1797,6 +1797,16 @@ api:
version_added: 2.2.0
example: ~
default: ""
+ access_control_allow_credentials:
+ description: |
+ Whether the FastAPI server includes the
``Access-Control-Allow-Credentials`` header on
+ CORS responses. Defaults to True to preserve existing behavior; set to
False if you have
+ configured ``access_control_allow_origins`` and do not want browsers
to send credentials
+ (cookies, Authorization header) with cross-origin requests.
+ type: boolean
+ version_added: 3.2.2
+ example: ~
+ default: "True"
grid_view_sorting_order:
description: |
Sorting order in grid view. Valid values are: ``topological``,
``hierarchical_alphabetical``
diff --git a/airflow-core/tests/unit/api_fastapi/core_api/test_app.py
b/airflow-core/tests/unit/api_fastapi/core_api/test_app.py
index 061ac788d8d..7a999e0f3b3 100644
--- a/airflow-core/tests/unit/api_fastapi/core_api/test_app.py
+++ b/airflow-core/tests/unit/api_fastapi/core_api/test_app.py
@@ -21,15 +21,19 @@ import inspect
import typing
import pytest
+from fastapi import FastAPI
+from fastapi.middleware.cors import CORSMiddleware
from fastapi.params import Depends as DependsClass
from fastapi.responses import StreamingResponse
from starlette.routing import Mount
from airflow.api_fastapi.app import create_app
+from airflow.api_fastapi.core_api.app import init_config
from airflow.api_fastapi.core_api.routes.public import authenticated_router
from airflow.api_fastapi.core_api.routes.ui import ui_router
from airflow.api_fastapi.core_api.security import get_user
+from tests_common.test_utils.config import conf_vars
from tests_common.test_utils.db import clear_db_jobs
pytestmark = pytest.mark.db_test
@@ -143,3 +147,22 @@ class TestRouterLevelDefaultDeny:
"ui_router must declare Depends(get_user) at the router level so
every UI endpoint "
"default-denies unauthenticated requests."
)
+
+
+class TestCorsMiddlewareAllowCredentials:
+ @pytest.mark.parametrize(
+ ("config_value", "expected_allow_credentials"),
+ [(None, True), ("True", True), ("False", False)],
+ )
+ def test_init_config_passes_allow_credentials(self, config_value,
expected_allow_credentials):
+ config = {("api", "access_control_allow_origins"):
"https://example.com"}
+ if config_value is not None:
+ config[("api", "access_control_allow_credentials")] = config_value
+
+ with conf_vars(config):
+ app = FastAPI()
+ init_config(app)
+
+ cors_middlewares = [m for m in app.user_middleware if m.cls is
CORSMiddleware]
+ assert len(cors_middlewares) == 1
+ assert cors_middlewares[0].kwargs["allow_credentials"] is
expected_allow_credentials