This is an automated email from the ASF dual-hosted git repository.
eladkal 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 f663060f405 Fix AUTH_ROLE_PUBLIC returning 401 in FastAPI API server
(#69773)
f663060f405 is described below
commit f663060f405c14445cf5098aa95e47a684ad1173
Author: John Doe <[email protected]>
AuthorDate: Tue Jul 14 00:17:27 2026 +0900
Fix AUTH_ROLE_PUBLIC returning 401 in FastAPI API server (#69773)
* Fix AUTH_ROLE_PUBLIC returning 401 in FastAPI API server
FabAuthRolePublicMiddleware only sets request.state.user for
anonymous requests. Core's get_user() dependency also checks
request.state.user_authenticated_via for the trusted-middleware
sentinel before skipping JWT validation, so every anonymous request
falls through to JWT validation, has no token, and is rejected with
401 regardless of AUTH_ROLE_PUBLIC (or [fab] auth_role_public) being
configured correctly.
Set the same trust marker JWTRefreshMiddleware already sets when it
pre-authenticates a request, so both middlewares honor the same
contract.
* Fix FAB middleware compatibility with Airflow<3.3
The FAB provider supports Airflow 3.0.2 and later, while
USER_INJECTED_BY_TRUSTED_MIDDLEWARE is only available in newer core
releases. Importing it unconditionally prevents the provider from loading
and causes test collection to fail against supported versions such as
Airflow 3.0.6.
Preserve trusted middleware authentication on newer core versions without
requiring older supported versions to expose the marker.
r Airflow versions
* fix: reflect modifications based on comments
* fix: reflect modifications based on comments
* Update providers/fab/src/airflow/providers/fab/auth_manager/middleware.py
---------
Co-authored-by: Elad Kalif <[email protected]>
---
.../src/airflow/providers/fab/auth_manager/middleware.py | 11 +++++++++++
.../fab/tests/unit/fab/auth_manager/test_middleware.py | 13 +++++++++++++
2 files changed, 24 insertions(+)
diff --git a/providers/fab/src/airflow/providers/fab/auth_manager/middleware.py
b/providers/fab/src/airflow/providers/fab/auth_manager/middleware.py
index 3bf2cea833c..e16b1af85bb 100644
--- a/providers/fab/src/airflow/providers/fab/auth_manager/middleware.py
+++ b/providers/fab/src/airflow/providers/fab/auth_manager/middleware.py
@@ -23,6 +23,7 @@ from starlette.middleware.base import BaseHTTPMiddleware
from airflow.api_fastapi.app import get_auth_manager
from airflow.api_fastapi.auth.managers.base_auth_manager import
COOKIE_NAME_JWT_TOKEN
+from airflow.api_fastapi.core_api import security as core_api_security
if TYPE_CHECKING:
from fastapi import Request
@@ -44,9 +45,19 @@ class FabAuthRolePublicMiddleware(BaseHTTPMiddleware):
async def dispatch(self, request: Request, call_next):
if not self._has_auth_token(request):
auth_manager = cast("FabAuthManager", get_auth_manager())
+
public_user = auth_manager.build_public_user()
if public_user is not None:
request.state.user = public_user
+
+ user_injected = getattr(
+ core_api_security,
+ "USER_INJECTED_BY_TRUSTED_MIDDLEWARE",
+ None,
+ )
+ if user_injected is not None:
+ request.state.user_authenticated_via = user_injected
+
return await call_next(request)
@staticmethod
diff --git a/providers/fab/tests/unit/fab/auth_manager/test_middleware.py
b/providers/fab/tests/unit/fab/auth_manager/test_middleware.py
index e4117a0fe91..b6dc2665531 100644
--- a/providers/fab/tests/unit/fab/auth_manager/test_middleware.py
+++ b/providers/fab/tests/unit/fab/auth_manager/test_middleware.py
@@ -21,6 +21,7 @@ from unittest.mock import AsyncMock, Mock, patch
import pytest
from airflow.api_fastapi.auth.managers.base_auth_manager import
COOKIE_NAME_JWT_TOKEN
+from airflow.api_fastapi.core_api import security as core_api_security
from airflow.providers.fab.auth_manager.middleware import
FabAuthRolePublicMiddleware
@@ -47,6 +48,18 @@ class TestFabAuthRolePublicMiddleware:
await middleware.dispatch(request, call_next)
assert request.state.user is public_user
+
+ trusted_marker = getattr(
+ core_api_security,
+ "USER_INJECTED_BY_TRUSTED_MIDDLEWARE",
+ None,
+ )
+
+ if trusted_marker is not None:
+ assert request.state.user_authenticated_via is trusted_marker
+ else:
+ assert not hasattr(request.state, "user_authenticated_via")
+
auth_manager.build_public_user.assert_called_once_with()
call_next.assert_awaited_once_with(request)