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

pierrejeambrun 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 b1aec757ce1 Revoke JWT on /auth/logout regardless of auth manager 
logout URL (#67289)
b1aec757ce1 is described below

commit b1aec757ce1e3800b629f36d4fbc274a48698412
Author: Pierre Jeambrun <[email protected]>
AuthorDate: Sat May 23 00:45:43 2026 +0200

    Revoke JWT on /auth/logout regardless of auth manager logout URL (#67289)
    
    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.
---
 .../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