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

potiuk 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 11f348694fc Fix SimpleAuthManager redirect to next URL after login 
(#67483)
11f348694fc is described below

commit 11f348694fc3024b81888e29618c25091fa41f66
Author: Aditya Patel <[email protected]>
AuthorDate: Mon May 25 17:10:25 2026 -0400

    Fix SimpleAuthManager redirect to next URL after login (#67483)
    
    * Fix simple auth redirect to next URL
    
    * Format simple auth redirect test
---
 .../auth/managers/simple/routes/login.py           |  6 +++++-
 .../auth/managers/simple/routes/test_login.py      | 22 ++++++++++++++++++++++
 2 files changed, 27 insertions(+), 1 deletion(-)

diff --git 
a/airflow-core/src/airflow/api_fastapi/auth/managers/simple/routes/login.py 
b/airflow-core/src/airflow/api_fastapi/auth/managers/simple/routes/login.py
index fc5938a14cc..c659edb0e19 100644
--- a/airflow-core/src/airflow/api_fastapi/auth/managers/simple/routes/login.py
+++ b/airflow-core/src/airflow/api_fastapi/auth/managers/simple/routes/login.py
@@ -28,6 +28,7 @@ from airflow.api_fastapi.auth.managers.simple.utils import 
parse_login_body
 from airflow.api_fastapi.common.router import AirflowRouter
 from airflow.api_fastapi.common.types import Mimetype
 from airflow.api_fastapi.core_api.openapi.exceptions import 
create_openapi_http_exception_doc
+from airflow.api_fastapi.core_api.security import is_safe_url
 from airflow.configuration import conf
 
 login_router = AirflowRouter(tags=["SimpleAuthManagerLogin"])
@@ -85,7 +86,10 @@ def create_token_all_admins() -> LoginResponse:
 )
 def login_all_admins(request: Request) -> RedirectResponse:
     """Login the user with no credentials."""
-    response = RedirectResponse(url=conf.get("api", "base_url", fallback="/"))
+    fallback_url = conf.get("api", "base_url", fallback="/")
+    next_url = request.query_params.get("next")
+    redirect_url = next_url if next_url and is_safe_url(next_url, 
request=request) else fallback_url
+    response = RedirectResponse(url=redirect_url)
 
     # The default config has this as an empty string, so we can't use 
`has_option`.
     # And look at the request info (needs `--proxy-headers` flag to api-server)
diff --git 
a/airflow-core/tests/unit/api_fastapi/auth/managers/simple/routes/test_login.py 
b/airflow-core/tests/unit/api_fastapi/auth/managers/simple/routes/test_login.py
index 85c24cf8ac8..4460f3e5fd4 100644
--- 
a/airflow-core/tests/unit/api_fastapi/auth/managers/simple/routes/test_login.py
+++ 
b/airflow-core/tests/unit/api_fastapi/auth/managers/simple/routes/test_login.py
@@ -87,6 +87,28 @@ class TestLogin:
             assert response.cookies.get("_token") is not None
             assert "samesite=lax" in response.headers["set-cookie"].lower()
 
+    def test_login_all_admins_redirects_to_next_url(self, test_client):
+        with conf_vars({("core", "simple_auth_manager_all_admins"): "true", 
("api", "ssl_cert"): "false"}):
+            response = test_client.get(
+                
"/auth/token/login?next=/dags/example_dag/runs/manual__2026-05-20/tasks/example_task",
+                follow_redirects=False,
+            )
+            assert response.status_code == 307
+            assert (
+                response.headers["location"] == 
"/dags/example_dag/runs/manual__2026-05-20/tasks/example_task"
+            )
+            assert response.cookies.get("_token") is not None
+
+    def test_login_all_admins_ignores_unsafe_next_url(self, test_client):
+        with conf_vars({("core", "simple_auth_manager_all_admins"): "true", 
("api", "ssl_cert"): "false"}):
+            response = test_client.get(
+                "/auth/token/login?next=https://example.com/malicious";,
+                follow_redirects=False,
+            )
+            assert response.status_code == 307
+            assert response.headers["location"] == "/"
+            assert response.cookies.get("_token") is not None
+
     def test_login_all_admins_config_disabled(self, test_client):
         response = test_client.get("/auth/token/login", follow_redirects=False)
         assert response.status_code == 403

Reply via email to