This is an automated email from the ASF dual-hosted git repository.
vincbeck 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 c902fea1145 Fix FabAuthManager.get_url_login() dropping next_url
(#73134)
c902fea1145 is described below
commit c902fea1145290e6e47c3a5c8b2901e945ae5219
Author: TheoLauw <[email protected]>
AuthorDate: Mon Sep 14 18:05:05 2026 +0200
Fix FabAuthManager.get_url_login() dropping next_url (#73134)
FabAuthManager.get_url_login() accepted **kwargs but never read
next_url from it, so callers redirecting an unauthenticated or
expired-session request to the login page (via
providers/fab/src/airflow/providers/fab/www/auth.py, which already
passes next_url=request.url) always lost the originally requested
URL. After completing login, the user landed on the homepage instead
of the deep link they opened, and had to open the same link a second
time to reach it.
This mirrors the fix already applied to SimpleAuthManager in #67965,
which was scoped to SimpleAuthManager only and left FabAuthManager
with the identical bug.
The rest of the redirect chain (flask_appbuilder's AuthOAuthView/
AuthDBView reading `next` from the query string into the OAuth state,
and the redirect/get_safe_redirect monkey-patch in
providers/fab/www/extensions/init_appbuilder.py that sets the JWT
cookie and forwards to that URL) already works correctly, so
propagating next_url into the login URL is the only change needed.
---
.../airflow/providers/fab/auth_manager/fab_auth_manager.py | 8 ++++++--
.../fab/tests/unit/fab/auth_manager/test_fab_auth_manager.py | 12 ++++++++++++
2 files changed, 18 insertions(+), 2 deletions(-)
diff --git
a/providers/fab/src/airflow/providers/fab/auth_manager/fab_auth_manager.py
b/providers/fab/src/airflow/providers/fab/auth_manager/fab_auth_manager.py
index 7e7241e48f0..629b974a2c1 100644
--- a/providers/fab/src/airflow/providers/fab/auth_manager/fab_auth_manager.py
+++ b/providers/fab/src/airflow/providers/fab/auth_manager/fab_auth_manager.py
@@ -23,7 +23,7 @@ import warnings
from contextlib import suppress
from functools import cached_property
from typing import TYPE_CHECKING, Any
-from urllib.parse import urljoin
+from urllib.parse import urlencode, urljoin
from cachetools import TTLCache, cachedmethod
from fastapi import FastAPI
@@ -706,7 +706,11 @@ class FabAuthManager(BaseAuthManager[User]):
def get_url_login(self, **kwargs) -> str:
"""Return the login page url."""
- return urljoin(self.apiserver_endpoint,
f"{AUTH_MANAGER_FASTAPI_APP_PREFIX}/login/")
+ login_url = urljoin(self.apiserver_endpoint,
f"{AUTH_MANAGER_FASTAPI_APP_PREFIX}/login/")
+ next_url = kwargs.get("next_url")
+ if next_url:
+ return f"{login_url}?{urlencode({'next': next_url})}"
+ return login_url
def get_url_logout(self) -> str | None:
"""Return the logout page url."""
diff --git a/providers/fab/tests/unit/fab/auth_manager/test_fab_auth_manager.py
b/providers/fab/tests/unit/fab/auth_manager/test_fab_auth_manager.py
index 5ddad4a1ae8..8f5d537039f 100644
--- a/providers/fab/tests/unit/fab/auth_manager/test_fab_auth_manager.py
+++ b/providers/fab/tests/unit/fab/auth_manager/test_fab_auth_manager.py
@@ -22,6 +22,7 @@ from itertools import chain
from typing import TYPE_CHECKING
from unittest import mock
from unittest.mock import MagicMock, Mock
+from urllib.parse import urlencode
import pytest
from flask import g
@@ -1053,6 +1054,17 @@ class TestFabAuthManager:
result = auth_manager.get_url_login()
assert result == f"{AUTH_MANAGER_FASTAPI_APP_PREFIX}/login/"
+ def test_get_url_login_with_next_url(self, auth_manager):
+ next_url =
"http://localhost:8080/dags/example_dag/runs/manual__2026-05-20/tasks/example_task"
+ result = auth_manager.get_url_login(next_url=next_url)
+ assert result ==
f"{AUTH_MANAGER_FASTAPI_APP_PREFIX}/login/?{urlencode({'next': next_url})}"
+
+ def test_get_url_login_without_next_url_kwarg(self, auth_manager):
+ # Callers that don't pass next_url (or pass an empty one) must keep
getting the
+ # bare login url, matching the pre-existing behavior relied on
elsewhere.
+ result = auth_manager.get_url_login(next_url=None)
+ assert result == f"{AUTH_MANAGER_FASTAPI_APP_PREFIX}/login/"
+
def test_get_url_logout(self, auth_manager):
result = auth_manager.get_url_logout()
assert result == f"{AUTH_MANAGER_FASTAPI_APP_PREFIX}/logout"