aminghadersohi opened a new pull request, #45639:
URL: https://github.com/apache/superset/pull/45639

   ### SUMMARY
   
   **Symptom.** With FastMCP 4 (pinned at 4.1.2 since 
https://github.com/apache/superset/pull/45415), an embedded guest token that 
the MCP transport accepts fails at tool time with `ValueError: JWT 
authenticated user not found in Superset database.` when the 
`CompositeTokenVerifier` is wrapped in FastMCP's `MultiAuth` (for example by an 
`MCP_AUTH_FACTORY` that combines an OAuth server with Superset's verifier). 
API-key tokens behind `MultiAuth` fail the same way.
   
   **Root cause.** FastMCP 4's `MultiAuth.verify_token` 
(`fastmcp/server/auth/auth.py:895-904` in fastmcp 4.1.2) replaces 
`AccessToken.client_id` with a source-qualified JSON string 
(`["configured:<sha256>","guest",null,null]`) and keeps the verifier's value in 
a new `original_client_id` field. FastMCP 3's `MultiAuth` returned the token 
unchanged. Superset compares the raw `client_id`:
   
   - `superset/mcp_service/auth.py:650`: `client_id == "guest"` gates the guest 
branch, so the guest token skips it, and the default resolver finds no username 
claim and falls back to `client_id` 
(`superset/mcp_service/mcp_config.py:957-958`), looking up the JSON string as a 
username: "user not found".
   - `superset/mcp_service/auth.py:678` and `:826`: `client_id == "api_key"` 
gates the API-key pass-through, so a validated API key is also resolved as a 
JWT username.
   
   The default auth factory does not use `MultiAuth`, so the plain composite 
path is not affected.
   
   **Fix.**
   
   - Add `get_verifier_client_id()` (`composite_token_verifier.py`): 
`original_client_id` when FastMCP set it, otherwise `client_id` (FastMCP 3, or 
no `MultiAuth`). Use it for the guest check, both API-key checks, and the 
default resolver's `client_id` fallback.
   - `original_client_id` is whatever the wrapped source set, and only the 
`CompositeTokenVerifier` strips the guest marker from JWTs. A token source 
placed next to it in `MultiAuth` could pass an IdP JWT through with both the 
marker and `client_id="guest"`. So the guest branch also re-verifies the raw 
bearer with `security_manager.parse_jwt_guest_token` (`GUEST_TOKEN_JWT_SECRET`, 
algorithm, audience, expiry, `type == "guest"`). It builds the `GuestUser` from 
the decoded claims instead of the `AccessToken` copy, and fails closed 
(`ValueError`) when the bearer is not a guest token. The API-key checks keep 
their existing guard. The API-key raw token is redacted after first use, so it 
cannot be re-verified the same way.
   
   Custom `MCP_USER_RESOLVER` callables that read `access_token.client_id` see 
the qualified form under `MultiAuth` on FastMCP 4. They can call 
`get_verifier_client_id(access_token)` instead.
   
   ### BEFORE/AFTER SCREENSHOTS OR ANIMATED GIF
   
   N/A (auth resolution, no UI).
   
   ### TESTING INSTRUCTIONS
   
   New tests in `tests/unit_tests/mcp_service/test_multi_auth_client_id.py` 
mint a real HS256 guest token and run it through the real `GuestTokenVerifier` 
→ `CompositeTokenVerifier` → FastMCP `MultiAuth` chain, then call 
`_resolve_user_from_jwt_context`:
   
   - `test_guest_token_resolves_as_guest[plain|multi_auth]`: a real guest token 
resolves to a `GuestUser`.
   - `test_forged_guest_marker_cannot_pose_as_guest[plain|multi_auth]`: a token 
with the marker and `client_id="guest"` whose bearer is signed with another key 
raises `not a valid guest token`, and `get_guest_user_from_token` is never 
called.
   - `test_guest_resolution_uses_verified_claims`: resources edited on the 
`AccessToken` do not reach the `GuestUser`.
   - `test_api_key_fast_path_through_multi_auth`, 
`test_api_key_fallback_through_multi_auth`: API keys behind `MultiAuth` still 
resolve.
   - `test_default_user_resolver_uses_unqualified_client_id`, 
`test_get_verifier_client_id_without_original_attribute`.
   
   ```bash
   pytest tests/unit_tests/mcp_service/test_multi_auth_client_id.py
   ```
   
   Before the fix (master `aabddf5b3f`, fastmcp 4.1.2 / mcp 2.3.0): 8 failed, 1 
passed (`test_guest_token_resolves_as_guest[plain]`). The guest case fails with:
   
   ```
   FAILED 
tests/unit_tests/mcp_service/test_multi_auth_client_id.py::test_guest_token_resolves_as_guest[multi_auth]
   E           ValueError: JWT authenticated user not found in Superset 
database. Ensure the user exists before granting MCP access.
   ```
   
   After the fix: 9 passed. `pytest tests/unit_tests/mcp_service/`: 11418 
passed, 12 skipped (fastmcp 4.1.2). The guest, API-key, user-resolution and 
composite-verifier test files plus the new file also pass on fastmcp 3.4.7 / 
mcp 1.30.0 (145 passed). With the bearer re-verification removed, the two 
forged-marker tests and `test_guest_resolution_uses_verified_claims` fail.
   
   ### ADDITIONAL INFORMATION
   
   - [ ] Has associated issue:
   - [ ] Required feature flags:
   - [ ] Changes UI
   - [ ] Includes DB Migration (follow approval process in 
[SIP-59](https://github.com/apache/superset/issues/13351))
     - [ ] Migration is atomic, supports rollback & is backwards-compatible
     - [ ] Confirm DB migration upgrade and downgrade tested
     - [ ] Runtime estimates and downtime expectations provided
   - [ ] Introduces new feature or API
   - [ ] Removes existing feature or API
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to