You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@superset.apache.org by "yves-renier (via GitHub)" <gi...@apache.org> on 2026/10/23 17:03:22 UTC

[D] RBAC-only with a non-empty scope for the MCP server [superset]

GitHub user yves-renier created a discussion: RBAC-only with a non-empty scope for the MCP server

### Bug description

Hello,
I Have set up RBAC for the MCP server, but I get errors on tools which declare no permissions (attached bellow). 

I understand the problem comes from my Identity provider which does not accept an empty scope and that a token with a non-empty scope enable scope checks.

I'm able to bypass that problem by modifying the `_get_token_scopes()` function in `superset/mcp_service/auth.py` and would like to propose a PR, but I'm not sure what would be preferred:

- limiting the scopes to valid superset scopes (as in _METHOD_TO_REQUIRED_SCOPE.values())
- or adding a variable to exclude specific scopes (for example MCP_IGNORED_SCOPES=[...])

The idea being that if the only scopes present in the token are discarded by the chosen criteria above, then _get_token_scopes() function return None disabling scope checks.

Would that be acceptable and if yes, what is the preferred solution ?

### Screenshots/recordings

_No response_

### Superset version

master / latest-dev

### Python version

3.12

### Node version

18 or greater

### Browser

Firefox

### Additional context

`superset_config.py`
```python
# Enable JWT authentication
MCP_AUTH_ENABLED = True
# Enable role based accreditations
MCP_RBAC_ENABLED = True
```
error in logs:
> WARNING:superset.mcp_service.auth:Tool quickstart_prompt is permission-protected but declares no class_permission_name; allowing access without an RBAC check
> WARNING:superset.mcp_service.auth:Scope denied for permission-less tool quickstart_prompt: token lacks required scope for method read

### Checklist

- [x] I have searched Superset docs and Slack and didn't find a solution to my problem.
- [x] I have searched the GitHub issue tracker and didn't find a similar bug report.
- [x] I have checked Superset's logs for errors and if I found a relevant Python stacktrace, I included it here as text in the "additional context" section.

GitHub link: https://github.com/apache/superset/discussions/45673

----
This is an automatically sent email for notifications@superset.apache.org.
To unsubscribe, please send an email to: notifications-unsubscribe@superset.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@superset.apache.org
For additional commands, e-mail: notifications-help@superset.apache.org


Re: [D] RBAC-only with a non-empty scope for the MCP server [superset]

Posted by "yves-renier (via GitHub)" <gi...@apache.org>.
GitHub user yves-renier added a comment to the discussion: RBAC-only with a non-empty scope for the MCP server

Hello.
Thank  for the guidance on setting superset scopes on the IdP side, but it is not really an option for us.
I expected some security impact to my first solution, that's why I also proposed the second (with explicit ignored scopes). 
As it seems to be the favored one, I will work on a PR.
I have to figure out how to document it properly, nevertheless I will link to the PR in this discussion once it is ready.

GitHub link: https://github.com/apache/superset/discussions/45673#discussioncomment-19213446

----
This is an automatically sent email for notifications@superset.apache.org.
To unsubscribe, please send an email to: notifications-unsubscribe@superset.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@superset.apache.org
For additional commands, e-mail: notifications-help@superset.apache.org


Re: [D] RBAC-only with a non-empty scope for the MCP server [superset]

Posted by "FrancescoCastaldi (via GitHub)" <gi...@apache.org>.
GitHub user FrancescoCastaldi added a comment to the discussion: RBAC-only with a non-empty scope for the MCP server

The issue happens because `_get_token_scopes()` in `superset/mcp_service/auth.py` returns `None` (which triggers the pure RBAC fallback) only when the token has no scopes at all. As soon as standard OIDC identity scopes like `openid` or `profile` are present, `_token_scope_allows()` evaluates them against Superset permissions. For tools that do not define an explicit `class_permission_name` (such as `quickstart_prompt`), the logic falls back to requiring the broad method scope `superset:read` or `superset:write`. Because standard IdP scopes do not match `superset:read`, the request is denied.

Between the two PR approaches you suggested, filtering out non-Superset scopes by returning `None` when no recognized scopes remain carries a security trade-off: an administrator who intends to restrict access using a scoped token would inadvertently have scope enforcement completely bypassed if their scope names are mistyped or unrecognized, dropping straight down to pure RBAC.

A cleaner, maintainer-aligned architecture consists of two parts:

1. **Explicit Scope Exclusion or Translation in Configuration:**
In `superset_config.py`, support an explicit list of generic identity scopes to ignore before evaluating permission scopes, for example:
```python
# superset_config.py
MCP_IGNORED_SCOPES = {"openid", "profile", "email", "offline_access"}
```
In `_get_token_scopes()`, strip those identity scopes first. If the remaining set of scopes is empty, and the deployment is configured for RBAC-only fallback, you can safely return `None`.

2. **Assigning Base Class Permissions to Permission-less Tools:**
In `superset/mcp_service/server.py`, assigning an explicit class permission to tools like `quickstart_prompt` (such as `class_permission_name="Superset"`) ensures that standard RBAC permissions (`can_read on Superset`) govern the tool consistently without triggering the permission-less scope denial.

If your IdP allows custom scope mappings, the immediate solution without modifying Superset core is to configure the client scope mapping in your IdP to append `superset:read` (and `superset:write` if needed) to the issued access token.


GitHub link: https://github.com/apache/superset/discussions/45673#discussioncomment-19164442

----
This is an automatically sent email for notifications@superset.apache.org.
To unsubscribe, please send an email to: notifications-unsubscribe@superset.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@superset.apache.org
For additional commands, e-mail: notifications-help@superset.apache.org


Re: [D] RBAC-only with a non-empty scope for the MCP server [superset]

Posted by "zuhairhalawa (via GitHub)" <gi...@apache.org>.
GitHub user zuhairhalawa added a comment to the discussion: RBAC-only with a non-empty scope for the MCP server

This is the scope check layered on top of RBAC in `superset/mcp_service/auth.py`. `_get_token_scopes()` returns `None` (scope checks skipped) only when the token carries no scopes at all. As soon as the token has any scope, `_token_scope_allows()` requires the flat scope for the tool's method: `superset:read` for `read`/`get`, `superset:write` for `write`/`update`/`delete`/`execute_sql_query`. Tools that declare a class permission can alternatively be satisfied by a per-resource scope such as `superset:dashboard:read`, but tools without `class_permission_name`, like `quickstart_prompt`, only have the flat scope to fall back on. That matches the "Scope denied for permission-less tool ... required scope for method read" line for a token that only carries something like `openid`.

So there is a way to get this working without changing Superset: have the IdP put `superset:read` (and `superset:write` if the write tools should be usable) on the token. That also gives the IdP the non-empty scope it insists on. The names come from `METHOD_PERMISSION_SCOPE_ACTION` in `superset/security/api_key_scopes.py`. `MCP_REQUIRED_SCOPES` is a different thing: it is passed to the JWT verifier as `required_scopes`, so it rejects tokens that lack those scopes, but it doesn't grant anything.

Which of the two PR shapes is preferred isn't stated in the code or docs, so that is a call for the maintainers. One consequence of the filtering approach worth noting: a token whose scopes are all unrecognised (or a mistyped `superset:*` scope) would be treated as unscoped and fall back to RBAC-only, so the restriction would be dropped instead of denied.

GitHub link: https://github.com/apache/superset/discussions/45673#discussioncomment-19161961

----
This is an automatically sent email for notifications@superset.apache.org.
To unsubscribe, please send an email to: notifications-unsubscribe@superset.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@superset.apache.org
For additional commands, e-mail: notifications-help@superset.apache.org


Re: [D] RBAC-only with a non-empty scope for the MCP server [superset]

Posted by "yves-renier (via GitHub)" <gi...@apache.org>.
GitHub user yves-renier added a comment to the discussion: RBAC-only with a non-empty scope for the MCP server

for context, here is my current fix implementing the first method (limiting the scopes to valid superset scopes) :

```python
def _get_token_scopes() -> set[str] | None:
    """Return the set of scopes on the current JWT access token, or None.

    Returns None when there is no JWT context or the token carries no scopes,
    so callers can fall back to RBAC-only behavior (back-compat for API-key and
    scope-less JWT deployments). Returns a (possibly populated) set only when
    the token explicitly advertises scopes.
    """
    try:
        from fastmcp.server.dependencies import get_access_token
    except ImportError:
        return None

    try:
        access_token = get_access_token()
    except Exception:  # noqa: BLE001 - fail closed on token-context errors
        logger.exception("Unable to resolve MCP access-token scopes")
        # ``None`` means that no scoped credential was presented and enables
        # legacy RBAC-only behavior. An empty set instead makes every scope
        # check fail, so an unexpected context error cannot erase restrictions
        # carried by a credential.
        return set()

    if access_token is None:
        return None
    
    #--------------------------------
    # MY MODIFICATIONS START HERE
    #--------------------------------

    scopes = getattr(access_token, "scopes", None)
    # keep only scopes used by superset
    valid_scopes = set(_METHOD_TO_REQUIRED_SCOPE.values())
    valid_scopes_from_token = {str(s) for s in scopes if s in valid_scopes}
    if not valid_scopes_from_token:
        # Token present but no valid scopes advertised: do NOT enforce scope checks.
        return None
    return valid_scopes_from_token
```

GitHub link: https://github.com/apache/superset/discussions/45673#discussioncomment-19150446

----
This is an automatically sent email for notifications@superset.apache.org.
To unsubscribe, please send an email to: notifications-unsubscribe@superset.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@superset.apache.org
For additional commands, e-mail: notifications-help@superset.apache.org


Re: [D] RBAC-only with a non-empty scope for the MCP server [superset]

Posted by "zuhairhalawa (via GitHub)" <gi...@apache.org>.
GitHub user zuhairhalawa added a comment to the discussion: RBAC-only with a non-empty scope for the MCP server

If setting the superset scopes on the IdP side works for you, or if this otherwise answered your question, could you mark my answer as accepted? That makes it easy for others with the same scope error to find.

GitHub link: https://github.com/apache/superset/discussions/45673#discussioncomment-19166874

----
This is an automatically sent email for notifications@superset.apache.org.
To unsubscribe, please send an email to: notifications-unsubscribe@superset.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@superset.apache.org
For additional commands, e-mail: notifications-help@superset.apache.org


Re: [D] RBAC-only with a non-empty scope for the MCP server [superset]

Posted by "zuhairhalawa (via GitHub)" <gi...@apache.org>.
GitHub user zuhairhalawa added a comment to the discussion: RBAC-only with a non-empty scope for the MCP server

One correction on the second suggestion: giving `quickstart_prompt` a `class_permission_name` would not avoid the scope denial. In `check_tool_permission` the scope check also applies to tools that have a class permission: the call is rejected when `has_permission and not _token_scope_allows(method_permission_name, class_permission_name)`. `_token_scope_allows` accepts either the flat scope (`superset:read` or `superset:write`) or the per-resource scope for a class listed in `RESOURCE_SCOPE_NAME` in `api_key_scopes.py`, and a name like `Superset` is not in that map. So a token that only carries `openid` is still denied; only the log line changes, from "Scope denied for permission-less tool" to "Scope denied for user". For a scoped token the ways around it are adding the `superset:*` scope on the IdP side, or changing how scopes are filtered.

GitHub link: https://github.com/apache/superset/discussions/45673#discussioncomment-19166869

----
This is an automatically sent email for notifications@superset.apache.org.
To unsubscribe, please send an email to: notifications-unsubscribe@superset.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@superset.apache.org
For additional commands, e-mail: notifications-help@superset.apache.org