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 677b4ab197c Drop the redundant OAuth2 branch from get_user and
collect_request_tokens (#72889)
677b4ab197c is described below
commit 677b4ab197c309fa2c3d210bd9536490f99ee753
Author: Pierre Jeambrun <[email protected]>
AuthorDate: Fri Sep 11 12:53:21 2026 +0200
Drop the redundant OAuth2 branch from get_user and collect_request_tokens
(#72889)
`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.
---
.../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 f825b066a16..f79d3da886a 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 796e501a0f3..df7a8abfb1a 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
@@ -206,17 +206,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
@@ -236,29 +227,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
@@ -272,7 +260,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)