Skip to content

Commit 6eb4d19

Browse files
pierrejeambrunYour friendly bot
authored andcommitted
[v3-2-test] Revoke JWT on /auth/logout regardless of auth manager logout URL (apache#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. (cherry picked from commit b1aec75) Co-authored-by: Pierre Jeambrun <pierrejbrun@gmail.com>
1 parent 351181e commit 6eb4d19

2 files changed

Lines changed: 29 additions & 4 deletions

File tree

  • airflow-core
    • src/airflow/api_fastapi/core_api/routes/public
    • tests/unit/api_fastapi/core_api/routes/public

airflow-core/src/airflow/api_fastapi/core_api/routes/public/auth.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -57,14 +57,15 @@ def login(request: Request, auth_manager: AuthManagerDep, next: None | str = Non
5757
)
5858
def logout(request: Request, auth_manager: AuthManagerDep) -> RedirectResponse:
5959
"""Logout the user."""
60+
# Revoke the current token before any redirect or cookie deletion so the JWT
61+
# is invalidated even when the auth manager redirects to an external logout URL.
62+
if token_str := request.cookies.get(COOKIE_NAME_JWT_TOKEN):
63+
auth_manager.revoke_token(token_str)
64+
6065
logout_url = auth_manager.get_url_logout()
6166
if logout_url:
6267
return RedirectResponse(logout_url)
6368

64-
# Revoke the current token before deleting the cookie
65-
if token_str := request.cookies.get(COOKIE_NAME_JWT_TOKEN):
66-
auth_manager.revoke_token(token_str)
67-
6869
secure = request.base_url.scheme == "https" or bool(conf.get("api", "ssl_cert", fallback=""))
6970
cookie_path = get_cookie_path()
7071
response = RedirectResponse(auth_manager.get_url_login())

airflow-core/tests/unit/api_fastapi/core_api/routes/public/test_auth.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,3 +220,27 @@ def test_logout_without_cookie_does_not_revoke(self, logout_client):
220220

221221
assert response.status_code == 307
222222
assert RevokedToken.is_revoked("nonexistent-jti") is False
223+
224+
def test_logout_revokes_token_when_logout_url_redirects(self, logout_client):
225+
"""Token must be revoked before the redirect when get_url_logout returns a URL."""
226+
now = int(time.time())
227+
auth_manager = logout_client.app.state.auth_manager
228+
signer = auth_manager._get_token_signer()
229+
token_payload = {
230+
"sub": "admin",
231+
"jti": "test-jti-redirect-456",
232+
"exp": now + 3600,
233+
"iat": now,
234+
"nbf": now,
235+
"aud": "apache-airflow",
236+
"iss": signer.issuer,
237+
}
238+
token_str = jwt.encode(token_payload, signer._secret_key, algorithm=signer.algorithm)
239+
240+
logout_client.cookies.set(COOKIE_NAME_JWT_TOKEN, token_str)
241+
with patch.object(auth_manager, "get_url_logout", return_value="http://external/logout"):
242+
response = logout_client.get("/auth/logout", follow_redirects=False)
243+
244+
assert response.status_code == 307
245+
assert response.headers["location"] == "http://external/logout"
246+
assert RevokedToken.is_revoked("test-jti-redirect-456") is True

0 commit comments

Comments
 (0)