Merge pull request #253 from MervinPraison/claude/issue-251-20260728-000000

fix: make URL-token auth opt-in so env token no longer forces 401
This commit is contained in:
praisonai-triage-agent[bot] authored and GitHub committed 2026-07-28 11:30:52 +00:00
commit 557852b287
2 files changed
+91 -4

No files matched your search

+24 -4
View File
@@ -2860,7 +2860,14 @@ def create_app(
require_auth: bool = False,
config_path: Optional[Path] = None,
) -> Starlette:
"""Create the Starlette application."""
"""Create the Starlette application.
URL-token auth (``TokenQueryMiddleware``) is opt-in: it activates only
when ``AIUI_REQUIRE_TOKEN`` is truthy, YAML ``auth.requireToken`` is set,
or ``require_auth=True``. A bare ``GATEWAY_AUTH_TOKEN`` /
``AIUI_URL_TOKEN`` env var (e.g. leaked into a developer shell after a
gateway run) no longer forces 401 on standalone ``/api/*`` (#238, #251).
"""
from praisonaiui.auth import (
AuthMiddleware,
login_handler,
@@ -2874,10 +2881,13 @@ def create_app(
config = load_config_from_yaml(config_path)
# Extract auth settings from config
_config_requires_token = False
if config:
auth_config = config.get("auth", {})
if auth_config.get("requireAuth") or auth_config.get("require_auth"):
require_auth = True
if auth_config.get("requireToken") or auth_config.get("require_token"):
_config_requires_token = True
middleware = [
Middleware(
@@ -2891,10 +2901,20 @@ def create_app(
# Add optional AUTH_ENFORCE middleware (env var driven)
middleware.append(Middleware(AuthEnforcementMiddleware))
# URL-token auth — opt-in via env. Enables query-param / cookie auth so
# dashboards can be shared by URL (Jupyter model).
# URL-token auth — opt-in. Enables query-param / cookie auth so dashboards
# can be shared by URL (Jupyter model). The mere presence of a token env
# var (e.g. GATEWAY_AUTH_TOKEN leaking into a developer shell after a
# gateway run) must NOT force auth on standalone create_app() — that broke
# ad-hoc TestClient scripts (#238, #251). Activation now requires an
# explicit opt-in: AIUI_REQUIRE_TOKEN=1, YAML auth.requireToken, or the
# require_auth flag.
_tok = os.environ.get("AIUI_URL_TOKEN") or os.environ.get("GATEWAY_AUTH_TOKEN")
if _tok:
_token_auth_opt_in = (
os.environ.get("AIUI_REQUIRE_TOKEN", "").lower() in ("true", "1", "yes")
or _config_requires_token
or require_auth
)
if _tok and _token_auth_opt_in:
from praisonaiui.auth import TokenQueryMiddleware
middleware.append(
Middleware(TokenQueryMiddleware, expected_token=_tok)
+67
View File
@@ -0,0 +1,67 @@
"""Regression tests for opt-in token auth activation (#238, #251).
The mere presence of GATEWAY_AUTH_TOKEN / AIUI_URL_TOKEN in the environment
(e.g. leaking into a developer shell after a gateway run) must not force 401
on standalone create_app() + TestClient. Token auth activates only when
explicitly opted in via AIUI_REQUIRE_TOKEN, YAML auth.requireToken, or
require_auth=True.
"""
import pytest
from starlette.testclient import TestClient
from praisonaiui import server
from praisonaiui.server import create_app
@pytest.fixture(autouse=True)
def _reset_state():
server.reset_state()
yield
server.reset_state()
def test_env_token_alone_does_not_force_401(monkeypatch):
monkeypatch.setenv("GATEWAY_AUTH_TOKEN", "test-secret-token")
monkeypatch.delenv("AIUI_REQUIRE_TOKEN", raising=False)
client = TestClient(create_app())
assert client.get("/api/agents").status_code == 200
def test_aiui_url_token_alone_does_not_force_401(monkeypatch):
monkeypatch.setenv("AIUI_URL_TOKEN", "test-secret-token")
monkeypatch.delenv("AIUI_REQUIRE_TOKEN", raising=False)
client = TestClient(create_app())
assert client.get("/api/agents").status_code == 200
def test_require_token_env_opt_in_enforces_401(monkeypatch):
monkeypatch.setenv("GATEWAY_AUTH_TOKEN", "test-secret-token")
monkeypatch.setenv("AIUI_REQUIRE_TOKEN", "1")
client = TestClient(create_app())
assert client.get("/api/agents").status_code == 401
def test_require_token_env_opt_in_allows_bearer(monkeypatch):
monkeypatch.setenv("GATEWAY_AUTH_TOKEN", "test-secret-token")
monkeypatch.setenv("AIUI_REQUIRE_TOKEN", "1")
client = TestClient(create_app())
resp = client.get(
"/api/agents",
headers={"Authorization": "Bearer test-secret-token"},
)
assert resp.status_code == 200
def test_config_require_token_opt_in_enforces_401(monkeypatch):
monkeypatch.setenv("AIUI_URL_TOKEN", "test-secret-token")
monkeypatch.delenv("AIUI_REQUIRE_TOKEN", raising=False)
client = TestClient(create_app(config={"auth": {"requireToken": True}}))
assert client.get("/api/agents").status_code == 401
def test_require_auth_opt_in_enforces_401(monkeypatch):
monkeypatch.setenv("AIUI_URL_TOKEN", "test-secret-token")
monkeypatch.delenv("AIUI_REQUIRE_TOKEN", raising=False)
client = TestClient(create_app(require_auth=True))
assert client.get("/api/agents").status_code == 401