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

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


The following commit(s) were added to refs/heads/v3-2-test by this push:
     new ef5787bc563 [v3-2-test] Revoke JWT on /auth/logout regardless of auth 
manager logout URL (#67289) (#67362)
ef5787bc563 is described below

commit ef5787bc563b973831bb45a2667b6e2b8e8ff67a
Author: github-actions[bot] 
<41898282+github-actions[bot]@users.noreply.github.com>
AuthorDate: Sat May 23 10:27:32 2026 +0800

    [v3-2-test] Revoke JWT on /auth/logout regardless of auth manager logout 
URL (#67289) (#67362)
    
    Previously, when an auth manager's get_url_logout() returned a URL, the
    /auth/logout endpoint short-circuited via early return and never invoked
    auth_manager.revoke_token(token_str). The JWT therefore remained valid
    after logout for auth managers like FabAuthManager and KeycloakAuthManager
    that redirect to an external logout URL.
    
    Move the revoke_token call before the early return so logout reliably
    invalidates the JWT token regardless of which auth manager is configured.
    (cherry picked from commit b1aec757ce1e3800b629f36d4fbc274a48698412)
    
    Co-authored-by: Pierre Jeambrun <[email protected]>
---
 .../api_fastapi/core_api/routes/public/auth.py     |  9 ++++----
 .../core_api/routes/public/test_auth.py            | 24 ++++++++++++++++++++++
 2 files changed, 29 insertions(+), 4 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 f85bcec3a61..6cf7529982c 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
@@ -57,14 +57,15 @@ def login(request: Request, auth_manager: AuthManagerDep, 
next: None | str = Non
 )
 def logout(request: Request, auth_manager: AuthManagerDep) -> RedirectResponse:
     """Logout the user."""
+    # Revoke the current token before any redirect or cookie deletion so the 
JWT
+    # is invalidated even when the auth manager redirects to an external 
logout URL.
+    if token_str := request.cookies.get(COOKIE_NAME_JWT_TOKEN):
+        auth_manager.revoke_token(token_str)
+
     logout_url = auth_manager.get_url_logout()
     if logout_url:
         return RedirectResponse(logout_url)
 
-    # Revoke the current token before deleting the cookie
-    if token_str := request.cookies.get(COOKIE_NAME_JWT_TOKEN):
-        auth_manager.revoke_token(token_str)
-
     secure = request.base_url.scheme == "https" or bool(conf.get("api", 
"ssl_cert", fallback=""))
     cookie_path = get_cookie_path()
     response = RedirectResponse(auth_manager.get_url_login())
diff --git 
a/airflow-core/tests/unit/api_fastapi/core_api/routes/public/test_auth.py 
b/airflow-core/tests/unit/api_fastapi/core_api/routes/public/test_auth.py
index ca0c87acd86..0379640f887 100644
--- a/airflow-core/tests/unit/api_fastapi/core_api/routes/public/test_auth.py
+++ b/airflow-core/tests/unit/api_fastapi/core_api/routes/public/test_auth.py
@@ -220,3 +220,27 @@ class TestLogoutTokenRevocation:
 
         assert response.status_code == 307
         assert RevokedToken.is_revoked("nonexistent-jti") is False
+
+    def test_logout_revokes_token_when_logout_url_redirects(self, 
logout_client):
+        """Token must be revoked before the redirect when get_url_logout 
returns a URL."""
+        now = int(time.time())
+        auth_manager = logout_client.app.state.auth_manager
+        signer = auth_manager._get_token_signer()
+        token_payload = {
+            "sub": "admin",
+            "jti": "test-jti-redirect-456",
+            "exp": now + 3600,
+            "iat": now,
+            "nbf": now,
+            "aud": "apache-airflow",
+            "iss": signer.issuer,
+        }
+        token_str = jwt.encode(token_payload, signer._secret_key, 
algorithm=signer.algorithm)
+
+        logout_client.cookies.set(COOKIE_NAME_JWT_TOKEN, token_str)
+        with patch.object(auth_manager, "get_url_logout", 
return_value="http://external/logout";):
+            response = logout_client.get("/auth/logout", 
follow_redirects=False)
+
+        assert response.status_code == 307
+        assert response.headers["location"] == "http://external/logout";
+        assert RevokedToken.is_revoked("test-jti-redirect-456") is True

Reply via email to