GitHub user aymansalkhatib deleted a comment on the discussion: MCP server: how to map the JWT identity to a Superset user? (MCP_USER_RESOLVER in docs, not found in code)
Short answer: `MCP_USER_RESOLVER` exists, but **not in the 6.1.0 code**. The 6.1.0 docs describe a feature that only ships from **7.0** onward (it's in `7.0.0rc1` and `7.0.0rc2`). That's why you see JWT validation succeed and then "No authenticated user found". ### What 6.1.0 actually does In the [6.1.0 `auth.py`](https://github.com/apache/superset/blob/6.1.0/superset/mcp_service/auth.py), `get_user_from_request()` only tries two sources: 1. `g.user`, if something already set it ("Preset workspace middleware", per the docstring); 2. `MCP_DEV_USERNAME`. Nothing reads the validated token's claims. `mcp_config.py` in 6.1.0 does define a `default_user_resolver()`, but `auth.py` never calls it, and `MCP_USER_RESOLVER` isn't referenced anywhere in that tag. So the JWT gets through the transport layer, no user is derived from it, and every call ends up either failing or running as the single `MCP_DEV_USERNAME` user. That matches what you describe. ### Where it was added On `master`, `auth.py` gained `_resolve_user_from_jwt_context()`, first in [#38747](https://github.com/apache/superset/pull/38747), with follow-ups such as #39106 and #39385. It reads the token through FastMCP's `get_access_token()` and calls: ```python resolver = app.config.get("MCP_USER_RESOLVER", default_user_resolver) username = resolver(app, access_token) ``` The [default resolver](https://github.com/apache/superset/blob/master/superset/mcp_service/mcp_config.py) checks `preferred_username`, then `username`, `email`, `sub`. The result is looked up as a Superset username, with an email fallback when it contains `@`. It **fails closed**: if the user doesn't exist, or the token has no usable claim, the call is denied and there's no fallback to `MCP_DEV_USERNAME`. Deactivated accounts are also refused. ### Mapping `sub` on 7.0 If your IdP's `sub` is an opaque ID, a custom resolver can return the Superset username yourself: ```python # superset_config.py def resolve_mcp_user(app, access_token): claims = getattr(access_token, "claims", None) or {} return claims.get("preferred_username") or claims.get("email") MCP_USER_RESOLVER = resolve_mcp_user ``` Users still have to exist in Superset beforehand; the resolver doesn't create them. If `MCP_JWT_ISSUER` trusts **more than one issuer**, 7.0 refuses to start without an issuer-aware resolver ([#42935](https://github.com/apache/superset/pull/42935), see `UPDATING.md`). In that case, include the `iss` claim in the identity you return. ### Until you can run 7.0 - **Test `7.0.0rc2`** in staging if you can. It contains the code above, and it's what the docs are describing. - On 6.1.0 there's no supported per-user mapping. The least-bad interim setup is **one MCP server instance per access level**, each with its own `MCP_DEV_USERNAME` pointing to a dedicated, least-privileged service account (for example `mcp_readonly_sales`, `mcp_readonly_finance`), with JWT auth kept on so only the right clients can reach each instance. You get role-level RBAC this way, but not per-person auditing. - If you need 6.1.x specifically, ask the maintainers about backporting #38747 and its follow-ups. It touches the auth path, so I wouldn't patch it locally. GitHub link: https://github.com/apache/superset/discussions/45457#discussioncomment-19065575 ---- This is an automatically sent email for [email protected]. To unsubscribe, please send an email to: [email protected] --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
