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)