You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@superset.apache.org by ru...@apache.org on 2026/06/09 16:19:21 UTC
(superset) branch master updated: fix(mcp): return a generic message when a request is unauthenticated (#40861)
This is an automated email from the ASF dual-hosted git repository.
rusackas pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/superset.git
The following commit(s) were added to refs/heads/master by this push:
new 9105adc67b2 fix(mcp): return a generic message when a request is unauthenticated (#40861)
9105adc67b2 is described below
commit 9105adc67b2d8a15835fc1d87d3c1534dd6a48ad
Author: Evan Rusackas <ev...@preset.io>
AuthorDate: Tue Jun 9 09:19:15 2026 -0700
fix(mcp): return a generic message when a request is unauthenticated (#40861)
Co-authored-by: Claude Code <no...@anthropic.com>
---
superset/mcp_service/auth.py | 23 +++++++----
tests/unit_tests/mcp_service/test_auth_api_key.py | 50 +++++++++++++++++++++++
2 files changed, 64 insertions(+), 9 deletions(-)
diff --git a/superset/mcp_service/auth.py b/superset/mcp_service/auth.py
index 7c3be500cd7..e9d4e950a04 100644
--- a/superset/mcp_service/auth.py
+++ b/superset/mcp_service/auth.py
@@ -495,21 +495,26 @@ def get_user_from_request() -> User:
if hasattr(g, "user") and g.user:
return g.user
- # No auth source available — log diagnostics server-side, raise generic
- # client-facing error so no config details leak toward the client.
+ # No auth source available. Keep the configuration diagnostics in the
+ # server logs only -- the message returned to the (unauthenticated) client
+ # must not reveal which auth mechanisms are configured.
auth_enabled = current_app.config.get("MCP_AUTH_ENABLED", False)
jwt_configured = bool(
current_app.config.get("MCP_JWKS_URI")
or current_app.config.get("MCP_JWT_PUBLIC_KEY")
or current_app.config.get("MCP_JWT_SECRET")
)
- logger.debug(
- "No auth source found. "
- "MCP_AUTH_ENABLED=%s, JWT keys configured=%s, "
- "MCP_DEV_USERNAME configured=%s",
- auth_enabled,
- jwt_configured,
- bool(current_app.config.get("MCP_DEV_USERNAME")),
+ details = [
+ f"No JWT access token in MCP request context "
+ f"(MCP_AUTH_ENABLED={auth_enabled}, "
+ f"JWT keys configured={jwt_configured})",
+ "No API key in Authorization header",
+ "MCP_DEV_USERNAME is not configured",
+ "g.user was not set by external middleware",
+ ]
+ logger.warning(
+ "MCP request could not be authenticated. Tried: %s",
+ "; ".join(details),
)
raise MCPNoAuthSourceError(
"Authentication required. No valid credentials provided."
diff --git a/tests/unit_tests/mcp_service/test_auth_api_key.py b/tests/unit_tests/mcp_service/test_auth_api_key.py
index 31901a26f72..3b88b6b7424 100644
--- a/tests/unit_tests/mcp_service/test_auth_api_key.py
+++ b/tests/unit_tests/mcp_service/test_auth_api_key.py
@@ -23,6 +23,7 @@ The streamable-http transport does not push a Flask request context, so
``flask.request``. These tests mock ``get_access_token`` accordingly.
"""
+import logging
from collections.abc import Generator
from contextlib import contextmanager
from unittest.mock import MagicMock, patch
@@ -183,6 +184,55 @@ def test_api_key_disabled_skips_auth(app: SupersetApp) -> None:
mock_sm.validate_api_key.assert_not_called()
+@pytest.mark.usefixtures("_disable_api_keys")
+def test_unauthenticated_error_does_not_leak_config(
+ app: SupersetApp, caplog: pytest.LogCaptureFixture
+) -> None:
+ """The error returned to an unauthenticated client must not reveal which
+ auth mechanisms are configured; the diagnostics stay in the server log."""
+ # Snapshot and restore config: the ``app`` fixture is module-scoped, so
+ # leaving these set would leak into later tests in this module.
+ sentinel = object()
+ saved_auth = app.config.get("MCP_AUTH_ENABLED", sentinel)
+ saved_secret = app.config.get("MCP_JWT_SECRET", sentinel)
+ app.config["MCP_AUTH_ENABLED"] = True
+ app.config["MCP_JWT_SECRET"] = "super-secret-value" # noqa: S105
+
+ try:
+ with app.test_request_context():
+ g.user = None
+ with caplog.at_level(logging.WARNING, logger="superset.mcp_service.auth"):
+ with pytest.raises(
+ MCPNoAuthSourceError, match="Authentication required"
+ ) as exc_info:
+ get_user_from_request()
+ finally:
+ for key, saved in (
+ ("MCP_AUTH_ENABLED", saved_auth),
+ ("MCP_JWT_SECRET", saved_secret),
+ ):
+ if saved is sentinel:
+ app.config.pop(key, None)
+ else:
+ app.config[key] = saved
+
+ message = str(exc_info.value)
+ assert message == "Authentication required. No valid credentials provided."
+ for leaked in (
+ "MCP_AUTH_ENABLED",
+ "JWT keys configured",
+ "API key",
+ "MCP_DEV_USERNAME",
+ "super-secret-value",
+ ):
+ assert leaked not in message
+
+ # The diagnostics the client must not see are still emitted server-side.
+ server_log = caplog.text
+ assert "could not be authenticated" in server_log
+ assert "MCP_AUTH_ENABLED=True" in server_log
+
+
# -- No AccessToken -> API key auth skipped --