This is an automated email from the ASF dual-hosted git repository.

vatsrahul1001 pushed a commit to branch v3-3-test
in repository https://gitbox.apache.org/repos/asf/airflow.git


The following commit(s) were added to refs/heads/v3-3-test by this push:
     new d3983249e40 [v3-3-test] Drop the redundant OAuth2 branch from get_user 
and collect_request_tokens (#72889) (#72943)
d3983249e40 is described below

commit d3983249e407fff4898d8cb25d38e906345ed3ff
Author: github-actions[bot] 
<41898282+github-actions[bot]@users.noreply.github.com>
AuthorDate: Fri Sep 11 17:24:24 2026 +0530

    [v3-3-test] Drop the redundant OAuth2 branch from get_user and 
collect_request_tokens (#72889) (#72943)
    
    `HTTPBearer` and `OAuth2PasswordBearer` both extract the same 
`Authorization:
    Bearer` header, so declaring both as dependencies produces the same string
    twice. `get_user` had a dead branch for the OAuth2 side (the bearer branch
    above it always matched first), and `collect_request_tokens` deduped the
    duplicate anyway.
    
    Keep `oauth2_scheme` declared as an unused parameter so the OpenAPI security
    spec is unchanged and ``/docs`` still renders the OAuth2 password login 
form.
    Only the runtime dead code goes.
    (cherry picked from commit 677b4ab197c309fa2c3d210bd9536490f99ee753)
    
    Co-authored-by: Pierre Jeambrun <[email protected]>
---
 .../api_fastapi/core_api/routes/public/auth.py     | 15 ++++------
 .../src/airflow/api_fastapi/core_api/security.py   |  9 +++---
 .../unit/api_fastapi/core_api/test_security.py     | 34 +++++++---------------
 3 files changed, 21 insertions(+), 37 deletions(-)

diff --git 
a/airflow-core/src/airflow/api_fastapi/core_api/routes/public/auth.py 
b/airflow-core/src/airflow/api_fastapi/core_api/routes/public/auth.py
index 890be3849fd..c10fb71d9c8 100644
--- a/airflow-core/src/airflow/api_fastapi/core_api/routes/public/auth.py
+++ b/airflow-core/src/airflow/api_fastapi/core_api/routes/public/auth.py
@@ -67,18 +67,15 @@ def login(request: Request, auth_manager: AuthManagerDep, 
next: None | str = Non
 def logout(
     request: Request,
     auth_manager: AuthManagerDep,
-    oauth_token: str | None = Depends(oauth2_scheme),
+    # Kept for the OpenAPI security spec so ``/docs`` still renders the OAuth2 
password
+    # login form. It resolves to the same ``Authorization: Bearer`` header
+    # ``bearer_scheme`` reads, so the value is unused at runtime.
+    _oauth_token: str | None = Depends(oauth2_scheme),
     bearer_credentials: HTTPAuthorizationCredentials | None = 
Depends(bearer_scheme),
 ) -> RedirectResponse:
     """Logout the user."""
-    # Revoke every credential presented before any redirect or cookie 
deletion, so the
-    # JWT is invalidated even when the auth manager redirects to an external 
logout URL.
-    #
-    # This previously read only the `_token` cookie. A client that 
authenticates with an
-    # `Authorization: Bearer` header -- the documented way to call the API -- 
therefore
-    # got a successful logout response while its token was never revoked, and 
the token
-    # stayed valid until it expired.
-    for token_str in collect_request_tokens(request, oauth_token, 
bearer_credentials):
+    # Invalidate both tokens from the Authorization header and the _token 
cookie, if present.
+    for token_str in collect_request_tokens(request, bearer_credentials):
         auth_manager.revoke_token(token_str)
 
     logout_url = auth_manager.get_url_logout()
diff --git a/airflow-core/src/airflow/api_fastapi/core_api/security.py 
b/airflow-core/src/airflow/api_fastapi/core_api/security.py
index 6a357789757..a78187be582 100644
--- a/airflow-core/src/airflow/api_fastapi/core_api/security.py
+++ b/airflow-core/src/airflow/api_fastapi/core_api/security.py
@@ -144,14 +144,15 @@ USER_INJECTED_BY_TRUSTED_MIDDLEWARE = object()
 
 async def get_user(
     request: Request,
-    oauth_token: str | None = Depends(oauth2_scheme),
+    # Kept for the OpenAPI security spec so ``/docs`` still renders the OAuth2 
password
+    # login form. It resolves to the same ``Authorization: Bearer`` header
+    # ``bearer_scheme`` reads, so the value is unused at runtime.
+    _oauth_token: str | None = Depends(oauth2_scheme),
     bearer_credentials: HTTPAuthorizationCredentials | None = 
Depends(bearer_scheme),
 ) -> BaseUser:
     # An explicitly supplied credential always wins over the ambient session 
cookie.
     if bearer_credentials and bearer_credentials.scheme.lower() == "bearer":
         return await resolve_user_from_token(bearer_credentials.credentials)
-    if oauth_token:
-        return await resolve_user_from_token(oauth_token)
 
     # No explicit credential on this request, so the cookie is the caller's 
identity.
     # A user might have been already built by a trusted in-tree middleware 
(currently
@@ -168,7 +169,6 @@ async def get_user(
 
 def collect_request_tokens(
     request: Request,
-    oauth_token: str | None,
     bearer_credentials: HTTPAuthorizationCredentials | None,
 ) -> list[str]:
     """
@@ -183,7 +183,6 @@ def collect_request_tokens(
     candidates: list[str | None] = []
     if bearer_credentials and bearer_credentials.scheme.lower() == "bearer":
         candidates.append(bearer_credentials.credentials)
-    candidates.append(oauth_token)
     candidates.append(request.cookies.get(COOKIE_NAME_JWT_TOKEN))
 
     tokens: list[str] = []
diff --git a/airflow-core/tests/unit/api_fastapi/core_api/test_security.py 
b/airflow-core/tests/unit/api_fastapi/core_api/test_security.py
index 83e8b6d3c33..8736b37e62e 100644
--- a/airflow-core/tests/unit/api_fastapi/core_api/test_security.py
+++ b/airflow-core/tests/unit/api_fastapi/core_api/test_security.py
@@ -198,17 +198,8 @@ class TestFastApiSecurity:
         assert result == resolved_user
         mock_resolve_user_from_token.assert_called_once_with("cookie_token")
 
-    @pytest.mark.parametrize(
-        ("oauth_token", "bearer_credentials_creds", "expected"),
-        [
-            pytest.param(None, "bearer_token", "bearer_token", id="bearer"),
-            pytest.param("oauth_token", None, "oauth_token", id="oauth"),
-        ],
-    )
     @patch("airflow.api_fastapi.core_api.security.resolve_user_from_token")
-    async def test_get_user_explicit_credential_beats_cookie_user(
-        self, mock_resolve_user_from_token, oauth_token, 
bearer_credentials_creds, expected
-    ):
+    async def test_get_user_explicit_credential_beats_cookie_user(self, 
mock_resolve_user_from_token):
         """An explicitly supplied credential wins over the cookie-derived 
session user.
 
         `JWTRefreshMiddleware` resolves a user from the `_token` cookie alone 
and stamps
@@ -228,29 +219,26 @@ class TestFastApiSecurity:
         request.state.user_authenticated_via = 
USER_INJECTED_BY_TRUSTED_MIDDLEWARE
         request.cookies = {COOKIE_NAME_JWT_TOKEN: "cookie_token"}
 
-        bearer_credentials = None
-        if bearer_credentials_creds:
-            bearer_credentials = Mock()
-            bearer_credentials.scheme = "bearer"
-            bearer_credentials.credentials = bearer_credentials_creds
+        bearer_credentials = Mock()
+        bearer_credentials.scheme = "bearer"
+        bearer_credentials.credentials = "bearer_token"
 
-        result = await get_user(request, oauth_token, bearer_credentials)
+        result = await get_user(request, None, bearer_credentials)
 
         assert result == token_user
         assert result != cookie_user
-        mock_resolve_user_from_token.assert_called_once_with(expected)
+        mock_resolve_user_from_token.assert_called_once_with("bearer_token")
 
     @pytest.mark.parametrize(
-        ("oauth_token", "bearer_credentials_creds", "cookies", "expected"),
+        ("bearer_credentials_creds", "cookies", "expected"),
         [
-            ("oauth_token", None, {}, "oauth_token"),
-            (None, "bearer_credentials_creds", {}, "bearer_credentials_creds"),
-            (None, None, {COOKIE_NAME_JWT_TOKEN: "cookie_token"}, 
"cookie_token"),
+            ("bearer_credentials_creds", {}, "bearer_credentials_creds"),
+            (None, {COOKIE_NAME_JWT_TOKEN: "cookie_token"}, "cookie_token"),
         ],
     )
     @patch("airflow.api_fastapi.core_api.security.resolve_user_from_token")
     async def test_get_user_with_token(
-        self, mock_resolve_user_from_token, oauth_token, 
bearer_credentials_creds, cookies, expected
+        self, mock_resolve_user_from_token, bearer_credentials_creds, cookies, 
expected
     ):
         user = Mock()
         mock_resolve_user_from_token.return_value = user
@@ -264,7 +252,7 @@ class TestFastApiSecurity:
             bearer_credentials.scheme = "bearer"
             bearer_credentials.credentials = bearer_credentials_creds
 
-        result = await get_user(request, oauth_token, bearer_credentials)
+        result = await get_user(request, None, bearer_credentials)
 
         assert result == user
         mock_resolve_user_from_token.assert_called_once_with(expected)

Reply via email to