Skip to content

fix(mcp): create ApiKey permissions on init and support API keys with JWT auth#39604

Open
aminghadersohi wants to merge 4 commits intoapache:masterfrom
aminghadersohi:amin/mcp-api-key-auth-fixes
Open

fix(mcp): create ApiKey permissions on init and support API keys with JWT auth#39604
aminghadersohi wants to merge 4 commits intoapache:masterfrom
aminghadersohi:amin/mcp-api-key-auth-fixes

Conversation

@aminghadersohi
Copy link
Copy Markdown
Contributor

SUMMARY

Two fixes for MCP API key authentication that were discovered while testing #39437:

1. superset init now creates ApiKey FAB permissions

When FAB_API_KEY_ENABLED=True, the /api/v1/security/api_keys/ endpoints returned 403 because the required FAB permissions (can_list, can_create, can_get, can_delete on ApiKey) were never created. Superset uses AppBuilder(update_perms=False), so FAB skips permission creation during blueprint registration. The fix adds ApiKey permissions to create_custom_permissions() in the SecurityManager, which is called by sync_role_definitions() during superset init.

2. CompositeTokenVerifier allows API key tokens to coexist with JWT auth

When MCP_AUTH_ENABLED=True, the JWTVerifier at the FastMCP transport layer rejected all non-JWT Bearer tokens (including sst_... API keys) before they could reach the Flask-level _resolve_user_from_api_key() handler. The new CompositeTokenVerifier wraps the JWT verifier and detects API key prefixes — matching tokens pass through with a marker claim so the existing auth priority chain handles validation.

Changes:

  • superset/security/manager.py: Add ApiKey permissions to create_custom_permissions() when FAB_API_KEY_ENABLED=True
  • superset/mcp_service/composite_token_verifier.py: New CompositeTokenVerifier that routes tokens by prefix
  • superset/mcp_service/mcp_config.py: Wrap JWT verifier with CompositeTokenVerifier when API keys enabled
  • superset/mcp_service/auth.py: Detect _api_key_passthrough claim in _resolve_user_from_jwt_context() and fall through to API key auth

BEFORE/AFTER SCREENSHOTS OR ANIMATED GIF

N/A — auth infrastructure changes with no UI impact.

TESTING INSTRUCTIONS

  1. Set FAB_API_KEY_ENABLED = True and FEATURE_FLAGS = {"FAB_API_KEY_ENABLED": True} in superset_config.py
  2. Run superset init — should create ApiKey permissions (verify: Admin role now has can_list, can_create, can_get, can_delete on ApiKey)
  3. Navigate to /profile/ — API Keys section should load without errors
  4. Create an API key via the UI
  5. Test with MCP (even with MCP_AUTH_ENABLED=True):
    curl -s -X POST http://localhost:5008/mcp \
      -H "Authorization: Bearer sst_<your-key>" \
      -H "Content-Type: application/json" \
      -H "Accept: application/json, text/event-stream" \
      -d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"call_tool","arguments":{"name":"get_instance_info","arguments":{}}}}'
  6. Run tests: pytest tests/unit_tests/mcp_service/test_auth_api_key.py tests/unit_tests/mcp_service/test_composite_token_verifier.py -v

ADDITIONAL INFORMATION

  • Has associated issue:
  • Required feature flags: FAB_API_KEY_ENABLED
  • Changes UI
  • Includes DB Migration
  • Introduces new feature or API
  • Removes existing feature or API

@bito-code-review
Copy link
Copy Markdown
Contributor

bito-code-review Bot commented Apr 23, 2026

Code Review Agent Run #a46f3e

Actionable Suggestions - 0
Review Details
  • Files reviewed - 6 · Commit Range: 6e34690..d7b92b1
    • superset/mcp_service/auth.py
    • superset/mcp_service/composite_token_verifier.py
    • superset/mcp_service/mcp_config.py
    • superset/security/manager.py
    • tests/unit_tests/mcp_service/test_auth_api_key.py
    • tests/unit_tests/mcp_service/test_composite_token_verifier.py
  • Files skipped - 0
  • Tools
    • Whispers (Secret Scanner) - ✔︎ Successful
    • Detect-secrets (Secret Scanner) - ✔︎ Successful
    • MyPy (Static Code Analysis) - ✔︎ Successful
    • Astral Ruff (Static Code Analysis) - ✔︎ Successful

Bito Usage Guide

Commands

Type the following command in the pull request comment and save the comment.

  • /review - Manually triggers a full AI review.

  • /pause - Pauses automatic reviews on this pull request.

  • /resume - Resumes automatic reviews.

  • /resolve - Marks all Bito-posted review comments as resolved.

  • /abort - Cancels all in-progress reviews.

Refer to the documentation for additional commands.

Configuration

This repository uses Superset You can customize the agent settings here or contact your Bito workspace admin at evan@preset.io.

Documentation & Help

AI Code Review powered by Bito Logo

@dosubot dosubot Bot added api Related to the REST API authentication Related to authentication authentication:fab Related to authentication with FAB change:backend Requires changing the backend labels Apr 23, 2026
Comment thread superset/mcp_service/mcp_config.py Fixed
Comment thread tests/unit_tests/mcp_service/test_auth_api_key.py Outdated
Comment thread tests/unit_tests/mcp_service/test_auth_api_key.py Outdated
Comment thread tests/unit_tests/mcp_service/test_auth_api_key.py Outdated
Comment thread tests/unit_tests/mcp_service/test_composite_token_verifier.py Outdated
Comment thread tests/unit_tests/mcp_service/test_composite_token_verifier.py Outdated
Comment thread tests/unit_tests/mcp_service/test_composite_token_verifier.py Outdated
@bito-code-review
Copy link
Copy Markdown
Contributor

The CodeQL alert indicates logging of sensitive data (password), but the code logs API key prefixes (e.g., ["sst_"]), which are configuration values, not actual passwords or secrets. This seems like a false positive. The info-level log aids debugging API key authentication setup.

superset/mcp_service/mcp_config.py

logger.info("API key auth enabled for MCP (prefixes: %s)", api_key_prefixes)

@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 23, 2026

Codecov Report

❌ Patch coverage is 0% with 26 lines in your changes missing coverage. Please review.
✅ Project coverage is 64.52%. Comparing base (7c4f876) to head (38828ab).
⚠️ Report is 64 commits behind head on master.

Files with missing lines Patch % Lines
superset/mcp_service/composite_token_verifier.py 0.00% 14 Missing ⚠️
superset/mcp_service/mcp_config.py 0.00% 5 Missing ⚠️
superset/mcp_service/auth.py 0.00% 4 Missing ⚠️
superset/security/manager.py 0.00% 2 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master   #39604      +/-   ##
==========================================
- Coverage   64.54%   64.52%   -0.02%     
==========================================
  Files        2565     2566       +1     
  Lines      133665   133691      +26     
  Branches    31056    31061       +5     
==========================================
  Hits        86269    86269              
- Misses      45904    45929      +25     
- Partials     1492     1493       +1     
Flag Coverage Δ
hive 39.81% <0.00%> (-0.02%) ⬇️
mysql 60.33% <0.00%> (-0.03%) ⬇️
postgres 60.41% <0.00%> (-0.03%) ⬇️
presto 41.58% <0.00%> (-0.02%) ⬇️
python 61.98% <0.00%> (-0.03%) ⬇️
sqlite 60.04% <0.00%> (-0.03%) ⬇️
unit 100.00% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@netlify
Copy link
Copy Markdown

netlify Bot commented Apr 23, 2026

Deploy Preview for superset-docs-preview ready!

Name Link
🔨 Latest commit ed62ca2
🔍 Latest deploy log https://app.netlify.com/projects/superset-docs-preview/deploys/69eab97c83d3b20008a7c7e2
😎 Deploy Preview https://deploy-preview-39604--superset-docs-preview.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@github-actions github-actions Bot removed the api Related to the REST API label Apr 24, 2026
@aminghadersohi
Copy link
Copy Markdown
Contributor Author

Addressed all review comments in the latest commit:

CodeQL (clear-text logging): False positive — the log line logs FAB_API_KEY_PREFIXES (e.g. ["sst_"]), which are configuration values/prefixes, not actual API keys or passwords. No sensitive data is exposed.

codeant-ai (type annotations): Added explicit type annotations to all new test function parameters and fixture return types across both test files. Fixtures now have return type annotations (-> MagicMock, -> CompositeTokenVerifier) and test functions annotate their fixture parameters (app: SupersetApp, composite_verifier: CompositeTokenVerifier, mock_jwt_verifier: MagicMock).

@aminghadersohi aminghadersohi force-pushed the amin/mcp-api-key-auth-fixes branch from 1cbd49c to ed62ca2 Compare April 24, 2026 00:29
@bito-code-review
Copy link
Copy Markdown
Contributor

bito-code-review Bot commented Apr 24, 2026

Code Review Agent Run #933d30

Actionable Suggestions - 0
Additional Suggestions - 1
  • tests/unit_tests/mcp_service/test_composite_token_verifier.py - 1
    • Incomplete test assertion · Line 102-102
      The assertion in `test_invalid_jwt_returns_none` checks that the JWT verifier was called but does not verify the token argument, which could mask bugs if an incorrect token is passed. Other similar tests in the file include the argument in the assertion for completeness.
      Code suggestion
       @@ -102,1 +102,1 @@
      -    mock_jwt_verifier.verify_token.assert_awaited_once()
      +    mock_jwt_verifier.verify_token.assert_awaited_once_with("not_a_valid_token")
Review Details
  • Files reviewed - 6 · Commit Range: 16c13b8..ed62ca2
    • superset/mcp_service/auth.py
    • superset/mcp_service/composite_token_verifier.py
    • superset/mcp_service/mcp_config.py
    • superset/security/manager.py
    • tests/unit_tests/mcp_service/test_auth_api_key.py
    • tests/unit_tests/mcp_service/test_composite_token_verifier.py
  • Files skipped - 0
  • Tools
    • Whispers (Secret Scanner) - ✔︎ Successful
    • Detect-secrets (Secret Scanner) - ✔︎ Successful
    • MyPy (Static Code Analysis) - ✔︎ Successful
    • Astral Ruff (Static Code Analysis) - ✔︎ Successful

Bito Usage Guide

Commands

Type the following command in the pull request comment and save the comment.

  • /review - Manually triggers a full AI review.

  • /pause - Pauses automatic reviews on this pull request.

  • /resume - Resumes automatic reviews.

  • /resolve - Marks all Bito-posted review comments as resolved.

  • /abort - Cancels all in-progress reviews.

Refer to the documentation for additional commands.

Configuration

This repository uses Superset You can customize the agent settings here or contact your Bito workspace admin at evan@preset.io.

Documentation & Help

AI Code Review powered by Bito Logo

… JWT auth

Two fixes for MCP API key authentication:

1. superset init now creates ApiKey FAB permissions (can_list, can_create,
   can_get, can_delete) when FAB_API_KEY_ENABLED=True. Previously, because
   Superset uses AppBuilder(update_perms=False), FAB skipped permission
   creation during blueprint registration and superset init never picked
   them up, causing 403 errors on /api/v1/security/api_keys/.

2. CompositeTokenVerifier allows API key tokens (e.g. sst_...) to coexist
   with JWT auth on the MCP transport layer. Previously, when
   MCP_AUTH_ENABLED=True, the JWTVerifier rejected all non-JWT Bearer
   tokens at the transport layer before they could reach the Flask-level
   _resolve_user_from_api_key() handler. The composite verifier detects
   API key prefixes and passes them through with a marker claim, letting
   the existing auth priority chain handle validation.
Wire CompositeTokenVerifier into create_default_mcp_auth_factory,
add _api_key_passthrough detection in _resolve_user_from_jwt_context,
create ApiKey permissions in create_custom_permissions, and update
test_auth_api_key with pass-through and non-matching prefix tests.
Address code review feedback: add explicit type annotations
to all new test function parameters and fixture return types.
Remove API key prefixes from log message to avoid CodeQL
false positive about clear-text logging of sensitive data.
@aminghadersohi aminghadersohi force-pushed the amin/mcp-api-key-auth-fixes branch from ed62ca2 to 38828ab Compare April 24, 2026 03:34
@bito-code-review
Copy link
Copy Markdown
Contributor

bito-code-review Bot commented Apr 24, 2026

Code Review Agent Run #257b47

Actionable Suggestions - 0
Review Details
  • Files reviewed - 6 · Commit Range: 868bc42..38828ab
    • superset/mcp_service/auth.py
    • superset/mcp_service/composite_token_verifier.py
    • superset/mcp_service/mcp_config.py
    • superset/security/manager.py
    • tests/unit_tests/mcp_service/test_auth_api_key.py
    • tests/unit_tests/mcp_service/test_composite_token_verifier.py
  • Files skipped - 0
  • Tools
    • Whispers (Secret Scanner) - ✔︎ Successful
    • Detect-secrets (Secret Scanner) - ✔︎ Successful
    • MyPy (Static Code Analysis) - ✔︎ Successful
    • Astral Ruff (Static Code Analysis) - ✔︎ Successful

Bito Usage Guide

Commands

Type the following command in the pull request comment and save the comment.

  • /review - Manually triggers a full AI review.

  • /pause - Pauses automatic reviews on this pull request.

  • /resume - Resumes automatic reviews.

  • /resolve - Marks all Bito-posted review comments as resolved.

  • /abort - Cancels all in-progress reviews.

Refer to the documentation for additional commands.

Configuration

This repository uses Superset You can customize the agent settings here or contact your Bito workspace admin at evan@preset.io.

Documentation & Help

AI Code Review powered by Bito Logo

@aminghadersohi
Copy link
Copy Markdown
Contributor Author

Review: CompositeTokenVerifier scope handling

Two findings from digging into the FastMCP internals:

🔴 Bug: pass-through tokens will 403 when MCP_JWT_REQUIRED_SCOPES is set

There are two independent scope enforcement layers in FastMCP:

  1. Inside verify_token()JWTVerifier.load_access_token() (providers/jwt.py:463-473) checks required_scopes before returning. The composite bypasses this for API key tokens — fine.
  2. Transport middlewareRequireAuthMiddleware.__call__() (bearer_auth.py:78-96) independently checks each required scope against AuthCredentials(auth_info.scopes). The pass-through AccessToken has scopes=[], so this check will 403 every API key request when MCP_JWT_REQUIRED_SCOPES is non-empty.

Fix: populate scopes from self.required_scopes on the pass-through token so the middleware is satisfied while _api_key_passthrough still tells _resolve_user_from_jwt_context to defer:

# composite_token_verifier.py
return AccessToken(
    token=token,
    client_id="api_key",
    scopes=list(self.required_scopes),  # satisfy RequireAuthMiddleware
    claims={"_api_key_passthrough": True},
)

🟡 Minor: _api_key_passthrough claim name collision

A JWT issued by an external IdP that happens to include {"_api_key_passthrough": true} as a custom claim would be silently misidentified in _resolve_user_from_jwt_context and cause auth failure. Not an auth bypass, but a subtle footgun. Consider a namespaced sentinel like _superset_mcp_api_key_passthrough or keying off client_id == "api_key" instead.


(base_url=None on super().__init__() for HS256 verifiers is fine — silently accepted, only suppresses RFC 9728 metadata routes which aren't needed here.)

@aminghadersohi aminghadersohi removed the request for review from eschutho May 5, 2026 19:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

authentication:fab Related to authentication with FAB authentication Related to authentication change:backend Requires changing the backend size/L

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants