Skip to content

fix(mcp): DCR must return srv-mcp's clientid, not the approuter's own - #2711

Merged
jung-thomas merged 1 commit into
DEVfrom
worktree-mcp-dcr-resource-clientid
Oct 11, 2026
Merged

jung-thomas merged 1 commit into
DEVfrom
worktree-mcp-dcr-resource-clientid

Conversation

@jung-thomas

Copy link
Copy Markdown
Contributor

What

Follow-up fix to #2710 (DCR). Live DEV probe of the DCR endpoint returned the wrong client_id — the approuter's own tutorials-identity clientid (cf2ea5eb…) instead of the one tutorials-srv-mcp validates token aud against (0b1e8b56…). A native-HTTP OAuth client registering via DCR would complete login and then hit WrongAudience 401 at /mcp-auth/api — the exact bug class #2710 set out to eliminate, just relocated into the proxy.

Root cause

resolveIasClientId() read the approuter's VCAP identity binding, on the (incorrect) #2593 assumption that the approuter binds the same identity instance as srv-mcp. It does not — verified on DEV 2026-10-11:

App identity instance clientid
tutorials-dev-approuter (runs /register) tutorials-approuter-identity cf2ea5eb…
tutorials-srv-mcp (validates aud) tutorials-identity 0b1e8b56…

Same tenant app_tid (3c6fa3f1…), distinct clients. @sap/xssec kind:ias checks aud === credentials.clientid exactly, so cf2ea5eb… can never pass srv-mcp.

Fix

resolveIasClientId() now prefers an explicit MCP_RESOURCE_CLIENT_ID env (srv-mcp's clientid, pinned per-env in the mtaext — DEV 0b1e8b56…, PROD 5a7cb5fd…), falling back to the VCAP binding only when unset. Deterministic; no shared-binding assumption. DCR response shape otherwise unchanged (public client, no secret).

Verification

  • Unit: 63 green (was 60) — +3 DCR tests (env precedence over VCAP, /register returns env value, empty-env falls back to VCAP).
  • mtaext: yq-validated, correct per-env clientids, app-name intact.
  • Post-DEV-deploy (pending): re-run POST /mcp-oauth/register → expect client_id = 0b1e8b56…; then native-HTTP OAuth end-to-end (Tom drives browser login).

🤖 Generated with Claude Code

Live DEV verification of #2710 (DCR) exposed the bug it was meant to kill,
relocated into the proxy. POST /mcp-oauth/register returned cf2ea5eb… — the
approuter's OWN tutorials-identity clientid — but tutorials-srv-mcp validates
token `aud` against 0b1e8b56… (its separate tutorials-identity instance). A
native-HTTP client registering via DCR would get cf2ea5eb…, complete login, and
hit WrongAudience 401 at /mcp-auth/api — the same aud-mismatch class, just moved.

Root cause: resolveIasClientId() trusted the approuter's VCAP identity binding,
on the (false) assumption from #2593 that the approuter binds the SAME identity
instance as srv-mcp. It does not — verified DEV 2026-10-11:
  - approuter  → instance tutorials-approuter-identity, clientid cf2ea5eb…
  - srv-mcp    → instance tutorials-identity,            clientid 0b1e8b56…
same tenant app_tid (3c6fa3f1…), distinct clients. @sap/xssec kind:ias checks
aud === credentials.clientid exactly, so cf2ea5eb… cannot pass srv-mcp.

Fix: resolveIasClientId() now prefers an explicit MCP_RESOURCE_CLIENT_ID env
(srv-mcp's clientid, pinned per-env in the mtaext — DEV 0b1e8b56…, PROD
5a7cb5fd…), falling back to the VCAP binding only when unset. Deterministic; no
shared-binding assumption. The DCR response is otherwise unchanged (public
client, no secret, redirect_uris echoed for the client↔proxy leg only).

- approuter/lib/mcp-consent.js: MCP_RESOURCE_CLIENT_ID precedence + corrected comment
- deploy/dev.mtaext + deploy/prod.mtaext: pin srv-mcp's per-env clientid
- tests: +3 (precedence over VCAP, /register returns the env value, empty-env
  falls back to VCAP); saveEnv tracks MCP_RESOURCE_CLIENT_ID; 63 green
@jung-thomas
jung-thomas merged commit 5ddf0e2 into DEV Oct 11, 2026
4 checks passed
@jung-thomas
jung-thomas deleted the worktree-mcp-dcr-resource-clientid branch October 11, 2026 16:56
jung-thomas added a commit that referenced this pull request Oct 11, 2026
release: MCP DCR + consent-proxy clientid fixes → PROD (#2709 #2710 #2711)
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