Skip to content

fix(mcp): accept MCP resource URL as valid OAuth audience#3459

Merged
saddlepaddle merged 1 commit into
mainfrom
mcp-auth
Apr 14, 2026
Merged

fix(mcp): accept MCP resource URL as valid OAuth audience#3459
saddlepaddle merged 1 commit into
mainfrom
mcp-auth

Conversation

@saddlepaddle
Copy link
Copy Markdown
Collaborator

@saddlepaddle saddlepaddle commented Apr 14, 2026

Summary

  • MCP clients on spec 2025-06-18 (incl. recent Claude Code) send resource=https://api.superset.sh/api/agent/mcp on authorize + token requests per RFC 8707. better-auth 1.5.6 validates that against validAudiences in checkResource() and throws requested resource invalid when it's not in the set. Our allowlist only contained the bare API origin, so OAuth sign-in from MCP clients broke with "Error: requested resource invalid".
  • Add the MCP endpoint to validAudiences in packages/auth/src/server.ts so better-auth mints tokens with the correct aud, and mirror the change in the MCP route's JWT verifier (apps/api/src/app/api/agent/[transport]/auth-flow.ts) so it accepts those tokens.
  • Drop two mock-heavy unit tests (auth-flow.test.ts, oauth-metadata.test.ts) that didn't catch this and would need hand-updating for the new audience shape.

Test plan

  • Deploy to preview, run /mcp against the Superset MCP server in Claude Code, confirm the OAuth browser flow completes and the server reports authenticated.
  • Sanity-check existing API-key and session-cookie auth paths still work against /api/agent/mcp.

Summary by cubic

Accept the MCP resource URL as a valid OAuth audience to restore OAuth sign-in for MCP clients (e.g., Claude Code). Tokens are now minted and verified with the MCP endpoint as an allowed aud.

  • Bug Fixes
    • Added https://api.superset.sh/api/agent/mcp to validAudiences in packages/auth/src/server.ts for better-auth.
    • Included the MCP URL in the JWT verifier’s audience list in apps/api/src/app/api/agent/[transport]/auth-flow.ts.
    • Removed outdated tests that didn’t cover the new audience shape (auth-flow.test.ts, oauth-metadata.test.ts).

Written for commit d212e6c. Summary will update on new commits.

Summary by CodeRabbit

Release Notes

  • Tests

    • Removed authentication flow test suite.
    • Removed OAuth metadata test suite.
  • Bug Fixes

    • Updated token validation configuration to accept additional audience values for agent authentication endpoints.

MCP clients on spec 2025-06-18 send resource=<mcp-url> in authorize/token
requests. better-auth 1.5.6 validates that against validAudiences and our
allowlist only contained the bare API origin, so tokens were rejected with
"requested resource invalid". Add the MCP endpoint to validAudiences and to
the JWT verifier's audience list.
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 14, 2026

📝 Walkthrough

Walkthrough

JWT token audience validation is expanded to accept a new audience value (${apiUrl}/api/agent/mcp) for MCP agent authentication endpoints, updated in both the API authentication flow and OAuth provider configuration. Two test files were deleted.

Changes

Cohort / File(s) Summary
MCP Authentication Audience Configuration
apps/api/src/app/api/agent/[transport]/auth-flow.ts, packages/auth/src/server.ts
Added new audience value ${apiUrl}/api/agent/mcp to JWT token verification and OAuth provider configuration, expanding the list of accepted audience values.
Test File Removals
apps/api/src/app/api/agent/[transport]/auth-flow.test.ts, apps/api/src/lib/oauth-metadata.test.ts
Deleted test suites covering authentication flow verification, token handling, and OAuth metadata construction (358 total lines removed).

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~15 minutes

Poem

🐰 A new audience joins the token dance,
/api/agent/mcp gets its chance,
Auth flows aligned, both left and right,
Tests bid farewell into the night,
Verification whispers through the scope!

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically summarizes the main change: adding the MCP resource URL as a valid OAuth audience to fix authentication flow for MCP clients.
Description check ✅ Passed The description includes a comprehensive summary of the problem, solution, and testing approach; however, it lacks explicit selections in the required 'Type of Change' template section.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch mcp-auth

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@greptile-apps
Copy link
Copy Markdown

greptile-apps Bot commented Apr 14, 2026

Greptile Summary

This PR fixes a broken OAuth sign-in path for MCP clients that conform to MCP spec 2025-06-18. Those clients include a resource parameter per RFC 8707 (Resource Indicators for OAuth 2.0), and better-auth 1.5.6 validates that value against validAudiences. Because the MCP endpoint URL was absent from the allowlist, every OAuth-based MCP connection was rejected with "requested resource invalid."

The fix is a symmetric two-line addition:

  • packages/auth/src/server.ts — adds ${env.NEXT_PUBLIC_API_URL}/api/agent/mcp to validAudiences so better-auth mints tokens scoped to the MCP resource.
  • apps/api/src/app/api/agent/[transport]/auth-flow.ts — adds the same URL to the audience array in verifyAccessToken's verifyOptions so the MCP route's JWT verifier accepts those tokens.

Both changes are minimal and correct.

Concerns:

  • The PR removes two test files (358 lines total) instead of updating them. auth-flow.test.ts contained the only assertion in the codebase that verified the exact audience array passed to verifyAccessToken — updating that one expectation would have proven the fix correct and guarded against regression.
  • oauth-metadata.test.ts tests pure utility functions with no audience dependency; none of its five tests required any modification. Deleting them removes coverage for proxy-header edge cases with no benefit.
  • The MCP path literal /api/agent/mcp is now duplicated across two separate packages, creating a future maintenance hazard.

Confidence Score: 4/5

Safe to merge — the two-line production fix is correct and minimal; the main concern is the deletion of meaningful test coverage rather than any correctness issue.

The actual bug fix is a straightforward, symmetrical addition to validAudiences and the JWT audience option with no logic errors. The score is 4 rather than 5 because the PR removes an assertion test that directly validates the audience array (the exact thing this PR changes), and separately deletes five utility tests that required zero updates — both of which are regressive. Restoring or updating those tests would make this a clean 5.

The deleted test files (auth-flow.test.ts and oauth-metadata.test.ts) deserve attention — particularly the audience-assertion test in auth-flow.test.ts that should be updated rather than removed.

Important Files Changed

Filename Overview
packages/auth/src/server.ts Adds the MCP endpoint to validAudiences in the oauthProvider plugin so better-auth accepts the RFC 8707 resource indicator from MCP clients; change is minimal and correct.
apps/api/src/app/api/agent/[transport]/auth-flow.ts Mirrors the audience expansion in the JWT verifier options so tokens minted with the MCP endpoint as aud are accepted by the MCP route; the change is correct and symmetrical with the auth server config.
apps/api/src/app/api/agent/[transport]/auth-flow.test.ts Entire test file deleted (296 lines, 10 tests); the deleted suite included a test that explicitly asserted the audience array passed to verifyAccessToken — removing it rather than updating it eliminates a valuable regression guard.
apps/api/src/lib/oauth-metadata.test.ts Entire test file deleted (62 lines, 5 tests) for pure utility helpers that have no dependency on the audience shape and required zero changes to remain valid.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[MCP Client Request] --> B{Has Bearer Token?}
    B -- No --> C[Return 401 with resource_metadata]
    C --> D[Client starts OAuth flow]
    D --> E[Authorization request with resource param]
    E --> F{validAudiences check in oauthProvider}
    F -- Invalid --> G[Error: requested resource invalid]
    F -- Valid after fix --> H[Authorization code issued]
    H --> I[Token request with resource param]
    I --> J[JWT minted with aud = resource URL]
    J --> K[Client retries with Bearer JWT]
    K --> B
    B -- Yes --> L{Is API key prefix?}
    L -- Yes --> M[Verify API key]
    L -- No --> N{Looks like JWT?}
    N -- Yes --> O{verifyAccessToken audience check}
    O -- Match after fix --> P[OAuth AuthInfo built]
    O -- No match --> Q[Try session fallback]
    N -- No --> Q
    P --> R[MCP transport handles request]
    Q --> R
Loading

Comments Outside Diff (2)

  1. apps/api/src/app/api/agent/[transport]/auth-flow.test.ts

    P1 Audience assertion test deleted instead of updated

    The deleted test "accepts OAuth access tokens before session lookup" (lines 133–175) contained the only assertion in the codebase that verified the exact verifyOptions.audience array passed to verifyAccessToken:

    expect(verifyAccessToken.mock.calls[0]?.[1]).toEqual({
      jwksUrl: "https://api.superset.sh/api/auth/jwks",
      verifyOptions: {
        issuer: "https://api.superset.sh",
        audience: ["https://api.superset.sh", "https://api.superset.sh/"],
      },
    });

    This test is precisely what would have caught the missing MCP audience in the first place. Rather than removing it, updating the expected audience array to include "https://api.superset.sh/api/agent/mcp" would both verify the fix and prevent the same regression from reappearing. The rest of the test suite (9 other tests covering API-key routing, session fallback, transport lifecycle) tests logic entirely unrelated to the audience shape and also required no changes.

  2. apps/api/src/lib/oauth-metadata.test.ts

    P1 Unrelated utility tests deleted without cause

    The PR description justifies the deletion by saying these tests "would need hand-updating for the new audience shape." That's not accurate for this file. Every test here exercises pure utility functions (getRequestOrigin, normalizeResourcePath, getOAuthProtectedResourceMetadataUrl, buildProtectedResourceMetadata) that have no dependency on the OAuth audience configuration:

    • getRequestOrigin reads x-forwarded-host / x-forwarded-proto headers.
    • normalizeResourcePath is a string normalization helper.
    • buildProtectedResourceMetadata constructs a metadata object.

    None of these functions changed in this PR, and none of the five tests would fail before or after this diff. Deleting them removes coverage for edge-cases like multi-value forwarded headers ("api.superset.sh, internal.example") that are meaningful in a proxied production environment.

Reviews (1): Last reviewed commit: "fix(mcp): accept MCP resource URL as val..." | Re-trigger Greptile

verifyOptions: {
issuer: apiUrl,
audience: [apiUrl, `${apiUrl}/`],
audience: [apiUrl, `${apiUrl}/`, `${apiUrl}/api/agent/mcp`],
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 MCP path duplicated across two packages

The literal /api/agent/mcp is now spelled out in both apps/api/src/app/api/agent/[transport]/auth-flow.ts (here) and packages/auth/src/server.ts. If the route path ever changes, or a second MCP transport is introduced, both files need updating independently.

Consider extracting the path into a shared constant in packages/shared (or similar) and importing it in both locations:

// packages/shared/src/mcp.ts
export const MCP_PATH = "/api/agent/mcp";
// auth-flow.ts
import { MCP_PATH } from "@superset/shared/mcp";
audience: [apiUrl, `${apiUrl}/`, `${apiUrl}${MCP_PATH}`],

// server.ts
import { MCP_PATH } from "@superset/shared/mcp";
validAudiences: [
  env.NEXT_PUBLIC_API_URL,
  `${env.NEXT_PUBLIC_API_URL}/`,
  `${env.NEXT_PUBLIC_API_URL}${MCP_PATH}`,
],

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
apps/api/src/app/api/agent/[transport]/auth-flow.ts (1)

224-224: Consider centralizing OAuth audience values to prevent config drift.

Line 224 duplicates audience values already configured in packages/auth/src/server.ts (Line 205-Line 209). A shared constant/helper for allowed audiences would reduce future auth breakage from one-sided edits.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/api/src/app/api/agent/`[transport]/auth-flow.ts at line 224, The inline
audience array in auth-flow.ts (the audience: [apiUrl, `${apiUrl}/`,
`${apiUrl}/api/agent/mcp`]) duplicates values defined in
packages/auth/src/server.ts; create a single exported constant (e.g.,
ALLOWED_OAUTH_AUDIENCES) in packages/auth (or a shared config module) that lists
the allowed audiences, export it from there, and replace the inline array in
auth-flow.ts with an import of that constant so both auth-server and the
auth-flow use the same source of truth.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/auth/src/server.ts`:
- Around line 205-209: The validAudiences array construction can produce a
double-slash when env.NEXT_PUBLIC_API_URL ends with '/', causing mismatched
audience strings; update the code that builds validAudiences (the validAudiences
array in this file) to normalize NEXT_PUBLIC_API_URL by trimming any trailing
slash before creating entries like `${base}/api/agent/mcp` and `${base}/` (or
construct audience URLs via a safe URL join/helper) so values never contain
`//api/...`.

---

Nitpick comments:
In `@apps/api/src/app/api/agent/`[transport]/auth-flow.ts:
- Line 224: The inline audience array in auth-flow.ts (the audience: [apiUrl,
`${apiUrl}/`, `${apiUrl}/api/agent/mcp`]) duplicates values defined in
packages/auth/src/server.ts; create a single exported constant (e.g.,
ALLOWED_OAUTH_AUDIENCES) in packages/auth (or a shared config module) that lists
the allowed audiences, export it from there, and replace the inline array in
auth-flow.ts with an import of that constant so both auth-server and the
auth-flow use the same source of truth.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 66c829ae-2702-42e8-9c29-d32e8b610de0

📥 Commits

Reviewing files that changed from the base of the PR and between 2c24d93 and d212e6c.

📒 Files selected for processing (4)
  • apps/api/src/app/api/agent/[transport]/auth-flow.test.ts
  • apps/api/src/app/api/agent/[transport]/auth-flow.ts
  • apps/api/src/lib/oauth-metadata.test.ts
  • packages/auth/src/server.ts
💤 Files with no reviewable changes (2)
  • apps/api/src/lib/oauth-metadata.test.ts
  • apps/api/src/app/api/agent/[transport]/auth-flow.test.ts

Comment on lines +205 to +209
validAudiences: [
env.NEXT_PUBLIC_API_URL,
`${env.NEXT_PUBLIC_API_URL}/`,
`${env.NEXT_PUBLIC_API_URL}/api/agent/mcp`,
],
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Normalize MCP audience construction to avoid //api/agent/mcp mismatches.

At Line 208, the audience is built from raw env.NEXT_PUBLIC_API_URL; if that env var ends with /, the result becomes ...//api/agent/mcp, which can break resource/audience matching.

Proposed fix
 			validAudiences: [
 				env.NEXT_PUBLIC_API_URL,
 				`${env.NEXT_PUBLIC_API_URL}/`,
-				`${env.NEXT_PUBLIC_API_URL}/api/agent/mcp`,
+				`${env.NEXT_PUBLIC_API_URL.replace(/\/+$/, "")}/api/agent/mcp`,
 			],
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
validAudiences: [
env.NEXT_PUBLIC_API_URL,
`${env.NEXT_PUBLIC_API_URL}/`,
`${env.NEXT_PUBLIC_API_URL}/api/agent/mcp`,
],
validAudiences: [
env.NEXT_PUBLIC_API_URL,
`${env.NEXT_PUBLIC_API_URL}/`,
`${env.NEXT_PUBLIC_API_URL.replace(/\/+$/, "")}/api/agent/mcp`,
],
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/auth/src/server.ts` around lines 205 - 209, The validAudiences array
construction can produce a double-slash when env.NEXT_PUBLIC_API_URL ends with
'/', causing mismatched audience strings; update the code that builds
validAudiences (the validAudiences array in this file) to normalize
NEXT_PUBLIC_API_URL by trimming any trailing slash before creating entries like
`${base}/api/agent/mcp` and `${base}/` (or construct audience URLs via a safe
URL join/helper) so values never contain `//api/...`.

Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No issues found across 4 files

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 14, 2026

🧹 Preview Cleanup Complete

The following preview resources have been cleaned up:

  • ✅ Neon database branch
  • ✅ Electric Fly.io app

Thank you for your contribution! 🎉

@saddlepaddle saddlepaddle merged commit 93140d9 into main Apr 14, 2026
15 checks passed
MocA-Love pushed a commit to MocA-Love/superset that referenced this pull request Apr 15, 2026
…h#3459)

MCP clients on spec 2025-06-18 send resource=<mcp-url> in authorize/token
requests. better-auth 1.5.6 validates that against validAudiences and our
allowlist only contained the bare API origin, so tokens were rejected with
"requested resource invalid". Add the MCP endpoint to validAudiences and to
the JWT verifier's audience list.
MocA-Love added a commit to MocA-Love/superset that referenced this pull request Apr 15, 2026
MocA-Love added a commit to MocA-Love/superset that referenced this pull request Apr 15, 2026
-s ours merge to record that upstream commits a3e34bf through
de70163 (13 commits) are semantically already present on origin/main
via the PR1-6 cherry-pick series (PRs #176, #177, #178, #179, #180,
#182), plus fork-adaptation fixes layered on top.

This merge target is de70163 specifically (not upstream/main) so
newer upstream commits (9fff075 and later) remain visible in future
behind counts.

Upstream commits covered by this audit merge:
- a3e34bf  fix(desktop): restore cmd+click requirement for v1 terminal file links (superset-sh#3457)  [PR1/#176]
- 57557f8  fix(desktop): gate v2 workspace children on collection readiness (superset-sh#3464)       [PR1/#176]
- 4ee2e61  fix(desktop): use native clipboard for copy path in v2 sidebar (superset-sh#3462)         [PR1/#176]
- 87d6e93  feat(desktop): close settings with Escape key (superset-sh#3466)                          [PR1/#176]
- 9c7f5f4  chore(desktop): auto-restart host-service on bundle change in dev (superset-sh#3461)      [PR1/#176]
- 93140d9  fix(mcp): accept MCP resource URL as valid OAuth audience (superset-sh#3459)              [PR2/#177]
- be9e000  fix(desktop): drive tray menu off events, fetch real org name (superset-sh#3458)          [PR2/#177]
- c5f791e  feat(v2): unify workspace delete through host-service (superset-sh#3443)                  [PR3/#178]
- 2c24d93  feat(desktop): paginated branch picker with checkout + open actions (superset-sh#3397)    [PR4/#179]
- 2bf1049  feat(desktop/hotkeys): v1 directional pane focus + best-effort v1 override migrator (superset-sh#3460)  [PR5/#180]
- 1294a7d  feat(desktop/hotkeys): restore Cmd+Alt+Arrow for tab/workspace nav (superset-sh#3472)    [PR5/#180]
- de70163  feat(desktop): v2 review tab first pass — PR info, checks, comments (superset-sh#3463)    [PR6/#182]

Intentionally skipped (version bump, fork has independent versioning):
- 1e23353  chore(desktop): bump version to 1.5.5 (superset-sh#3473)

Fork-adaptation fixes layered on top of the cherry-picks:
- PR1: host-service-coordinator alias import fix, settings Escape
       selector narrowing (role-based + popper wrapper), Escape
       close uses replace navigation
- PR2: dual quit mode preservation (requestQuit "release"/"stop"),
       trayUpdateToken guard for stale async fetchHostInfo results
- PR4: ChangesHeader.normalizeBranchName regex rewrite (lint false
       positive), worktree add uses fullRef for remote-tracking
       refs, syncTimedOut reset on pendingId change, GIT_REFS.md
       barrel example fix
- PR5: migrate.ts re-sanitize of existing localStorage overrides
       (v2 marker bump intent), FOCUS_PANE_* enabled:isActive for
       KeepAliveWorkspaces, CATEGORY_ORDER merges Navigation (upstream)
       and Browser (fork)
- PR6: normalizeThreadsToComments flattens all thread.comments (not
       just first), CommentPane overrides <a> (openUrl) and <img>
       (SafeImage), zero-badge suppression, pr-null comments gate

Fork features verified intact (Explore agent audit of combined
36d4de4..35d95f3 range):
- BROWSER_RELOAD / BROWSER_HARD_RELOAD hotkeys
- dual quit mode menu in tray
- v1 terminal cold-restore + retry reconnect (out of range but
  unaffected)
- KeepAliveWorkspaces (FOCUS_PANE_* gated on isActive)
- useCommandPalette + addMemoTab in v2 workspace
- host-service-coordinator rename alias pattern
@Kitenite Kitenite deleted the mcp-auth branch May 6, 2026 04:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant