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]
