Skip to content

Fix MCP auth ordering and protected-resource discovery#2887

Merged
Kitenite merged 4 commits into
superset-sh:mainfrom
Kitenite:renew-api-key-issue
Mar 26, 2026
Merged

Fix MCP auth ordering and protected-resource discovery#2887
Kitenite merged 4 commits into
superset-sh:mainfrom
Kitenite:renew-api-key-issue

Conversation

@Kitenite
Copy link
Copy Markdown
Collaborator

@Kitenite Kitenite commented Mar 25, 2026

Summary

  • split MCP auth into a dedicated flow so API keys, OAuth access tokens, and session auth are handled in the right order
  • return a path-specific resource_metadata challenge for /api/agent/mcp and serve matching protected-resource metadata
  • add focused tests for invalid API keys, valid API keys, OAuth access tokens, opaque bearer session tokens, and MCP auth failure behavior

Review Notes

During review I found and fixed two regressions before opening this PR:

  • the first protected-resource route change introduced an optional catch-all that Next 16 would not boot alongside the existing required catch-all under .well-known
  • opaque bearer session tokens were hitting OAuth verification first and producing false [mcp/auth] Access token verification failed errors before session fallback

Verification

  • bun test in apps/api
  • bun run typecheck in apps/api
  • bunx @biomejs/biome@2.4.2 check on changed files
  • local bun run --cwd apps/api dev smoke tests:
    • POST /api/agent/mcp with no auth returns 401 and a path-specific WWW-Authenticate challenge
    • POST /api/agent/mcp with Authorization: Bearer sk_live_invalid returns a fast 401 and logs the expected Better Auth invalid API key error
    • POST /api/agent/mcp with an opaque bearer token no longer produces a false OAuth verification error
    • /.well-known/oauth-protected-resource and /.well-known/oauth-protected-resource/api/agent/mcp return resource-specific metadata

Summary by cubic

Reordered MCP auth to check API keys first, then OAuth (JWKS), with session fallback, and only start the MCP transport on success. Added path‑specific OAuth protected‑resource discovery and WWW-Authenticate challenges for /api/agent/mcp, using forwarded host/proto.

  • New Features

    • Introduced auth-flow with ordered checks: API key (sk_live_), OAuth via JWKS (better-auth/oauth2), then session.
    • Served per-path metadata at /.well-known/oauth-protected-resource using oauth-metadata helpers with issuer and scopes from @superset/auth/server.
  • Bug Fixes

    • Accept case-insensitive Bearer and correctly parse x-forwarded-host/x-forwarded-proto lists when building origins and challenges.
    • Stopped false [mcp/auth] Access token verification failed logs by skipping OAuth verification for opaque session tokens and falling back correctly.
    • Fixed Next 16 catch‑all routing conflict; now serves root and [...path] protected-resource metadata.

Written for commit 6697ce9. Summary will update on new commits.

Summary by CodeRabbit

  • New Features

    • Dynamic OAuth server discovery and richer protected-resource metadata (scopes, resource name, path-specific URLs)
    • Helpers for request origin/path normalization and metadata construction
  • Refactor

    • Centralized MCP authentication and request handling with unified token/session verification and standardized 401 responses
  • Tests

    • Extensive tests covering auth flows, token variations, and OAuth metadata utilities
  • Chores

    • Added minimal typings for the test runner

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Mar 25, 2026

📝 Walkthrough

Walkthrough

Centralizes MCP authentication into a new auth-flow module (API key → OAuth/JWT → session fallback), delegates agent routes to it, adds OAuth protected-resource metadata helpers and tests, and updates .well-known routes to fetch OAuth server configuration and return metadata built via the new helpers.

Changes

Cohort / File(s) Summary
OAuth metadata utilities & tests
apps/api/src/lib/oauth-metadata.ts, apps/api/src/lib/oauth-metadata.test.ts
New helpers to compute request origin, normalize resource paths, build protected-resource metadata, and produce the metadata URL; tests validate header parsing, path normalization, and conditional metadata fields.
.well-known OAuth protected-resource routes
apps/api/src/app/.well-known/oauth-protected-resource/[...path]/route.ts, apps/api/src/app/.well-known/oauth-protected-resource/route.ts
Route handlers now await route params where applicable, fetch OAuth server config via auth.api.getOAuthServerConfig(request.headers), and return buildProtectedResourceMetadata(...) output including conditional issuer and scopes fields; removed manual public-origin logic.
MCP authentication core & tests
apps/api/src/app/api/agent/[transport]/auth-flow.ts, apps/api/src/app/api/agent/[transport]/auth-flow.test.ts
Adds centralized auth-flow: token parsing, API-key detection/verification, JWT/OAuth verification (JWKS), session fallback, verifyToken, unauthorizedResponse, and handleMcpRequest; tests cover multiple auth paths, WWW-Authenticate challenge, and transport handling.
Route handler delegation
apps/api/src/app/api/agent/[transport]/route.ts
Removed inline auth and error-response logic; routes now delegate to handleMcpRequest(req, deps) with shared deps (apiUrl, authApi, createServer, createTransport, verifyAccessToken).
Bun test typings
apps/api/src/bun-test.d.ts
Adds minimal TypeScript declarations for bun:test utilities (describe, it, mock, expect) used by the new tests.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant Route as Agent Route
    participant AuthFlow as verifyToken
    participant AuthAPI as Auth API
    participant OAuth as JWKS/OAuth Server
    participant Session as Session Store

    Client->>Route: Request to /api/agent/[transport]\nAuthorization: Bearer <token>
    Route->>AuthFlow: verifyToken(request, deps)

    alt API key (sk_live_...)
        AuthFlow->>AuthAPI: verifyApiKey(key)
        AuthAPI-->>AuthFlow: API key record
        AuthFlow-->>Route: AuthInfo (from API key)
    else JWT-like token
        AuthFlow->>OAuth: verifyAccessToken(token, jwksUrl)
        alt OAuth valid
            OAuth-->>AuthFlow: decoded claims
            AuthFlow-->>Route: AuthInfo (from token)
        else OAuth invalid
            AuthFlow->>Session: authApi.getSession(headers)
            Session-->>AuthFlow: session (user, activeOrganizationId)
            AuthFlow-->>Route: AuthInfo (from session) or undefined
        end
    else Fallback to session
        AuthFlow->>Session: authApi.getSession(headers)
        Session-->>AuthFlow: session data
        AuthFlow-->>Route: AuthInfo or undefined
    end

    alt Auth succeeded
        Route->>Route: createTransport/createServer
        Route->>Route: transport.handleRequest(req, { authInfo })
        Route-->>Client: 200 OK
    else Auth failed
        Route-->>Client: 401 Unauthorized\nWWW-Authenticate: resource_metadata="<origin>/.well-known/oauth-protected-resource/<path>"
    end
Loading
sequenceDiagram
    participant Client
    participant MetadataRoute as .well-known/oauth-protected-resource/[...path]
    participant Auth as auth.api
    participant OAuthConfig as OAuth Server Config
    participant Metadata as buildProtectedResourceMetadata

    Client->>MetadataRoute: GET /.well-known/oauth-protected-resource/… (request)
    MetadataRoute->>MetadataRoute: await params -> resourcePath
    MetadataRoute->>Auth: getOAuthServerConfig(request.headers)
    Auth->>OAuthConfig: fetch server metadata
    OAuthConfig-->>Auth: { issuer, scopes_supported, ... }
    Auth-->>MetadataRoute: authServerMetadata
    MetadataRoute->>Metadata: buildProtectedResourceMetadata(request, resourcePath, { authorizationServerUrl, resourceName, scopesSupported })
    Metadata-->>MetadataRoute: metadata object
    MetadataRoute-->>Client: 200 { resource, authorization_servers, scopes_supported?, resource_name? }
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Poem

🐰 I hop through headers, paths, and scopes,
Tokens sussed in burrows and hidden ropes.
Metadata blooms where routes once stood—
Keys, JWTs, sessions all understood.
Happy hops! 🥕

🚥 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 accurately and concisely summarizes the main changes: fixing MCP authentication ordering and adding protected-resource discovery endpoints.
Description check ✅ Passed The description is comprehensive and well-structured, covering the summary, related issues, type of change, testing, and additional notes with specific verification steps.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

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.

2 issues found across 8 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="apps/api/src/app/api/agent/[transport]/auth-flow.ts">

<violation number="1" location="apps/api/src/app/api/agent/[transport]/auth-flow.ts:49">
P2: Parse the `Authorization` scheme case-insensitively; strict `startsWith("Bearer ")` rejects valid lowercase/mixed-case bearer headers.</violation>
</file>

<file name="apps/api/src/lib/oauth-metadata.ts">

<violation number="1" location="apps/api/src/lib/oauth-metadata.ts:10">
P2: Parse forwarded host/proto header lists before composing the origin. Using raw `x-forwarded-*` values can generate malformed `resource_metadata` URLs behind multi-proxy deployments.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Comment thread apps/api/src/app/api/agent/[transport]/auth-flow.ts Outdated
Comment thread apps/api/src/lib/oauth-metadata.ts Outdated
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.

🧹 Nitpick comments (4)
apps/api/src/lib/oauth-metadata.ts (1)

33-37: Consider using a more specific return type.

Record<string, unknown> loses type information. A discriminated union or explicit interface would improve type safety for consumers.

💡 Suggested type
interface ProtectedResourceMetadata {
	resource: string;
	authorization_servers?: string[];
	scopes_supported?: string[];
	resource_name?: string;
	resource_documentation?: string;
}

export function buildProtectedResourceMetadata(
	req: Request,
	resourcePath: string,
	options: ProtectedResourceMetadataOptions,
): ProtectedResourceMetadata {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/api/src/lib/oauth-metadata.ts` around lines 33 - 37, The function
buildProtectedResourceMetadata currently returns a loose Record<string,
unknown>; define a concrete interface (e.g. ProtectedResourceMetadata with
fields resource, optional authorization_servers, scopes_supported,
resource_name, resource_documentation) and change the function signature to
return that interface instead of Record<string, unknown>. Add the new
ProtectedResourceMetadata interface near ProtectedResourceMetadataOptions and
adjust the function implementation to produce that shape, then update any call
sites or tests that rely on the previous loose type to use the new properties.
apps/api/src/app/.well-known/oauth-protected-resource/route.ts (1)

4-19: Consider handling errors from getOAuthServerConfig.

If auth.api.getOAuthServerConfig throws (e.g., due to misconfiguration or transient failure), the request will fail with a 500 error. Since this is a .well-known discovery endpoint, clients may retry frequently on failure.

Consider wrapping in try/catch to return partial metadata (without authorization server info) or a more graceful error response.

💡 Suggested approach
 export async function GET(request: Request): Promise<Response> {
-	const authServerMetadata = await auth.api.getOAuthServerConfig({
-		headers: request.headers,
-	});
+	let authServerMetadata: Awaited<ReturnType<typeof auth.api.getOAuthServerConfig>> | null = null;
+	try {
+		authServerMetadata = await auth.api.getOAuthServerConfig({
+			headers: request.headers,
+		});
+	} catch (error) {
+		console.error("[oauth-protected-resource] Failed to fetch OAuth server config:", error);
+	}

 	return Response.json(
 		buildProtectedResourceMetadata(request, "/", {
 			authorizationServerUrl:
-				typeof authServerMetadata.issuer === "string"
-					? authServerMetadata.issuer
+				typeof authServerMetadata?.issuer === "string"
+					? authServerMetadata?.issuer
 					: undefined,
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/api/src/app/.well-known/oauth-protected-resource/route.ts` around lines
4 - 19, Wrap the call to auth.api.getOAuthServerConfig inside a try/catch within
the GET handler so failures don't crash the endpoint; if getOAuthServerConfig
throws, catch the error (log it) and call
buildProtectedResourceMetadata(request, "/", { resourceName: "Superset MCP
Server", authorizationServerUrl: undefined, scopesSupported: undefined }) to
return partial discovery metadata instead of letting the request fail,
preserving existing behavior when the call succeeds by using
auth.api.getOAuthServerConfig().issuer and .scopes_supported as before.
apps/api/src/lib/oauth-metadata.test.ts (1)

35-50: Consider adding a test case for required-only metadata fields.

The test validates the full options case. Consider adding a test that verifies buildProtectedResourceMetadata correctly omits optional fields when they're not provided:

💡 Suggested test case
it("builds protected resource metadata with only required fields", () => {
	const request = new Request("https://api.superset.sh/anything");

	expect(
		buildProtectedResourceMetadata(request, "/api/agent/mcp", {}),
	).toEqual({
		resource: "https://api.superset.sh/api/agent/mcp",
	});
});
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/api/src/lib/oauth-metadata.test.ts` around lines 35 - 50, Add a new unit
test that calls buildProtectedResourceMetadata with only the required args
(e.g., new Request("https://api.superset.sh/anything"), path "/api/agent/mcp",
and an empty options object) and assert the result equals { resource:
"https://api.superset.sh/api/agent/mcp" } so optional fields
(authorization_servers, resource_name, scopes_supported) are omitted; implement
this as a new it("builds protected resource metadata with only required fields",
...) test alongside the existing tests to validate required-only behavior.
apps/api/src/bun-test.d.ts (1)

1-25: Consider adopting official Bun type definitions like other packages in the monorepo.

Other packages (shared, mcp, db, chat, etc.) already use @types/bun or bun-types directly. Adding @types/bun to apps/api's dependencies would eliminate the need to maintain custom declarations, provide complete type coverage (including beforeEach, afterEach, beforeAll, afterAll hooks and additional matchers), and automatically stay synchronized with Bun releases.

The current custom types are functional for existing tests and follow good practices (using unknown over any), but consolidating with official types would reduce maintenance overhead and align with patterns already established in the repository.

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

In `@apps/api/src/bun-test.d.ts` around lines 1 - 25, Replace the local custom
declaration for module "bun:test" with the official Bun types: remove or stop
exporting the custom declarations (MockResult, Mock, describe/it/mock/expect)
from apps/api/src/bun-test.d.ts and add the appropriate package (e.g.,
`@types/bun` or bun-types) to apps/api dependencies; then update the app's
tsconfig or project references to include the new types so the global Bun test
hooks and matchers (beforeEach/afterEach/beforeAll/afterAll and extended expect
matchers) are available. Ensure any code referencing MockResult/Mock or the
custom expect signatures still compiles after switching to the official types
and remove the custom file once validation is complete.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@apps/api/src/app/.well-known/oauth-protected-resource/route.ts`:
- Around line 4-19: Wrap the call to auth.api.getOAuthServerConfig inside a
try/catch within the GET handler so failures don't crash the endpoint; if
getOAuthServerConfig throws, catch the error (log it) and call
buildProtectedResourceMetadata(request, "/", { resourceName: "Superset MCP
Server", authorizationServerUrl: undefined, scopesSupported: undefined }) to
return partial discovery metadata instead of letting the request fail,
preserving existing behavior when the call succeeds by using
auth.api.getOAuthServerConfig().issuer and .scopes_supported as before.

In `@apps/api/src/bun-test.d.ts`:
- Around line 1-25: Replace the local custom declaration for module "bun:test"
with the official Bun types: remove or stop exporting the custom declarations
(MockResult, Mock, describe/it/mock/expect) from apps/api/src/bun-test.d.ts and
add the appropriate package (e.g., `@types/bun` or bun-types) to apps/api
dependencies; then update the app's tsconfig or project references to include
the new types so the global Bun test hooks and matchers
(beforeEach/afterEach/beforeAll/afterAll and extended expect matchers) are
available. Ensure any code referencing MockResult/Mock or the custom expect
signatures still compiles after switching to the official types and remove the
custom file once validation is complete.

In `@apps/api/src/lib/oauth-metadata.test.ts`:
- Around line 35-50: Add a new unit test that calls
buildProtectedResourceMetadata with only the required args (e.g., new
Request("https://api.superset.sh/anything"), path "/api/agent/mcp", and an empty
options object) and assert the result equals { resource:
"https://api.superset.sh/api/agent/mcp" } so optional fields
(authorization_servers, resource_name, scopes_supported) are omitted; implement
this as a new it("builds protected resource metadata with only required fields",
...) test alongside the existing tests to validate required-only behavior.

In `@apps/api/src/lib/oauth-metadata.ts`:
- Around line 33-37: The function buildProtectedResourceMetadata currently
returns a loose Record<string, unknown>; define a concrete interface (e.g.
ProtectedResourceMetadata with fields resource, optional authorization_servers,
scopes_supported, resource_name, resource_documentation) and change the function
signature to return that interface instead of Record<string, unknown>. Add the
new ProtectedResourceMetadata interface near ProtectedResourceMetadataOptions
and adjust the function implementation to produce that shape, then update any
call sites or tests that rely on the previous loose type to use the new
properties.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: c1858c41-125e-4ddf-a59a-88a7e92efc44

📥 Commits

Reviewing files that changed from the base of the PR and between 115c475 and 94d052a.

📒 Files selected for processing (8)
  • apps/api/src/app/.well-known/oauth-protected-resource/[...path]/route.ts
  • apps/api/src/app/.well-known/oauth-protected-resource/route.ts
  • apps/api/src/app/api/agent/[transport]/auth-flow.test.ts
  • apps/api/src/app/api/agent/[transport]/auth-flow.ts
  • apps/api/src/app/api/agent/[transport]/route.ts
  • apps/api/src/bun-test.d.ts
  • apps/api/src/lib/oauth-metadata.test.ts
  • apps/api/src/lib/oauth-metadata.ts

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: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/api/src/app/api/agent/`[transport]/auth-flow.ts:
- Around line 188-189: The catch blocks in the credential verification flow (the
catch handling API key/bearer verification where you currently call
console.error("[mcp/auth] API key verification failed:", error)) must stop
logging the raw error object; instead log only a sanitized identifier such as
error.name || error.code || 'AuthVerificationError' and an opaque message.
Replace the current console.error calls in those catch blocks (and the similar
one around lines handling token/claim verification) with a call that emits a
fixed message plus the sanitizedName only (no error.message, stack, or the error
object), e.g. build const sanitized = error && (error.name || error.code) ||
'AuthVerificationError' and log that value; do not include the original error or
any tokens/claims in the log output.
- Around line 197-202: The verifyAccessToken call is building jwksUrl and
audience from deps.apiUrl which may contain a trailing slash; normalize
deps.apiUrl (e.g., trim any trailing slash into a local normalizedApiUrl) before
constructing jwksUrl (`${normalizedApiUrl}/api/auth/jwks`) and the audience
array (`[normalizedApiUrl, `${normalizedApiUrl}/`]`) so URLs never end up with
double slashes; make this change where the token verification happens (around
the verifyAccessToken call using bearerToken) or add a small helper to normalize
deps.apiUrl before use.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 685404eb-9d5f-4c7c-bb51-4549b59c134e

📥 Commits

Reviewing files that changed from the base of the PR and between 94d052a and 6697ce9.

📒 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
  • apps/api/src/lib/oauth-metadata.ts
✅ Files skipped from review due to trivial changes (2)
  • apps/api/src/lib/oauth-metadata.test.ts
  • apps/api/src/lib/oauth-metadata.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/api/src/app/api/agent/[transport]/auth-flow.test.ts

Comment thread apps/api/src/app/api/agent/[transport]/auth-flow.ts Outdated
Comment thread apps/api/src/app/api/agent/[transport]/auth-flow.ts
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

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/api/src/app/api/agent/`[transport]/auth-flow.ts:
- Around line 228-240: The current flow falls back to session auth after
verifyAccessToken() succeeded if buildOAuthAuthInfo(bearerToken, payload)
returns undefined, allowing a verified-but-invalid OAuth token to be accepted
via cookie; change the logic in auth-flow.ts so that once verifyAccessToken()
succeeds you immediately return the result of buildOAuthAuthInfo (or
throw/return an error) instead of continuing to call deps.authApi.getSession and
buildSessionAuthInfo, and preserve oauthVerificationError for proper error
reporting; also add a regression test that constructs a verified token missing
required fields (e.g., no sub or no organizationId) to ensure the request fails
closed rather than falling back to session auth.
🪄 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: 2f736e77-2567-40e7-8197-46741e0749e2

📥 Commits

Reviewing files that changed from the base of the PR and between 6697ce9 and b607ea7.

📒 Files selected for processing (2)
  • apps/api/src/app/api/agent/[transport]/auth-flow.test.ts
  • apps/api/src/app/api/agent/[transport]/auth-flow.ts

Comment thread apps/api/src/app/api/agent/[transport]/auth-flow.ts Outdated
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.

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

279-295: Assert the forwarded authInfo, not just the startup path.

This still passes if handleMcpRequest() calls transport.handleRequest() without the authenticated context. I'd store the request and assert the second argument as well.

🤖 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.test.ts around lines 279 -
295, The test "starts MCP transport when auth succeeds" should also assert that
the authenticated context is forwarded to the transport: capture the arguments
passed to deps.transportHandleSpy (the saved call for transport.handleRequest
invoked by handleMcpRequest) and add an assertion that the second argument
contains the expected authInfo (e.g., user.id "user-4" and
session.activeOrganizationId "org-4"); update the test using
deps.transportHandleSpy.mock.calls or equivalent to fetch the second argument
and assert its auth properties alongside the existing call-count assertions so
the transport is proven to receive the authenticated context from
handleMcpRequest.

95-125: Cover the object-metadata branch too.

parseApiKeyMetadata() supports both JSON strings and already-parsed objects, but this suite only locks down the string case. A small object-shaped metadata test would keep the second supported input from drifting.

🤖 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.test.ts around lines 95 -
125, Add a test that exercises the object-shaped metadata branch of
parseApiKeyMetadata by mocking authApi.verifyApiKey to return key.metadata as an
already-parsed object (e.g. { organizationId: "org-1" }) instead of a JSON
string; use the same pattern as the "accepts valid API keys" test (createDeps,
verifyToken, createRequest) and assert that authInfo.extra.mcpContext includes
the expected organizationId and userId, ensuring parseApiKeyMetadata handles
object inputs correctly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@apps/api/src/app/api/agent/`[transport]/auth-flow.test.ts:
- Around line 279-295: The test "starts MCP transport when auth succeeds" should
also assert that the authenticated context is forwarded to the transport:
capture the arguments passed to deps.transportHandleSpy (the saved call for
transport.handleRequest invoked by handleMcpRequest) and add an assertion that
the second argument contains the expected authInfo (e.g., user.id "user-4" and
session.activeOrganizationId "org-4"); update the test using
deps.transportHandleSpy.mock.calls or equivalent to fetch the second argument
and assert its auth properties alongside the existing call-count assertions so
the transport is proven to receive the authenticated context from
handleMcpRequest.
- Around line 95-125: Add a test that exercises the object-shaped metadata
branch of parseApiKeyMetadata by mocking authApi.verifyApiKey to return
key.metadata as an already-parsed object (e.g. { organizationId: "org-1" })
instead of a JSON string; use the same pattern as the "accepts valid API keys"
test (createDeps, verifyToken, createRequest) and assert that
authInfo.extra.mcpContext includes the expected organizationId and userId,
ensuring parseApiKeyMetadata handles object inputs correctly.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 746435f8-6450-43e4-ab5b-931d25c0c1ea

📥 Commits

Reviewing files that changed from the base of the PR and between b607ea7 and c7282eb.

📒 Files selected for processing (2)
  • apps/api/src/app/api/agent/[transport]/auth-flow.test.ts
  • apps/api/src/app/api/agent/[transport]/auth-flow.ts

@Kitenite Kitenite merged commit 48b7861 into superset-sh:main Mar 26, 2026
13 of 15 checks passed
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