Ignore unsupported grant types in CIMD documents - #6297
Conversation
A Client ID Metadata Document describes the client's capabilities across every authorization server it talks to, and the client cannot tailor it per server. VS Code declares the device_code grant alongside authorization_code, and the CIMD resolver rejected the whole document over it, surfacing as an opaque invalid_client at /oauth/authorize. Filter unsupported grant_types and response_types out of CIMD documents instead of rejecting them, while still rejecting documents whose supported set does not intersect the one flow this server offers (authorization_code / code) — such a client could never complete a token exchange, and a clear error at resolution beats failing every token request. The stored fosite client carries only the filtered types, and dropped entries are logged at Debug for diagnosability. DCR requests keep the strict validation: unlike a CIMD document, a registration request is addressed to this server specifically, so rejecting an unsupported grant type is correct feedback there. Fixes #6290 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #6297 +/- ##
==========================================
+ Coverage 72.84% 72.86% +0.01%
==========================================
Files 743 743
Lines 77681 77707 +26
==========================================
+ Hits 56586 56618 +32
+ Misses 17129 17122 -7
- Partials 3966 3967 +1 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
|
@claude could you review the PR and make sure the fix follows the MCP specs about CIMD |
|
Claude finished @amirejaz's task in 3m 45s —— View job Review: CIMD grant/response type filtering vs. the MCP spec
VerdictThe fix is consistent with the MCP CIMD spec, and the reasoning in the godoc is the right one. No blocking issues. Two nits and three follow-up gaps of the same shape as #6290 that this PR (correctly) doesn't touch. Spec checkThe MCP draft's client-registration page lists exactly what an AS MUST do with a CIMD document:
Nothing there requires rejecting a document over
A portable, self-hosted document that the client cannot tailor per-AS must not be fatal on an entry one AS doesn't support. RFC 7591 §2 backs the mechanism too — "the authorization server MAY reject or replace any of the client's requested metadata values … and substitute them with suitable values" — filtering is substitution. Keeping Behavior check
Nits1. if len(grantTypes) < len(doc.GrantTypes) {2. Duplicate entries in a declared list survive the filter ( Follow-ups: same failure shape, other fieldsThese are all pre-existing and correctly outside this PR under the one-logical-change rule, but they're the next #6290s: a) The DCR path rejects the same client. b) c) Not worth acting on: TestsGood shape. The regression test pins the real VS Code document shape, the two table tests cover pass-through / filtered / rejected, and the |
Review follow-ups: the filters returned the package-level default slices unaliased into cached fosite clients (inconsistent with the scope path, which clones), and the drop-log condition bound a value it only compared against zero. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
jhrozek
left a comment
There was a problem hiding this comment.
Reviewed with security and code-quality passes (go-security-reviewer, code-reviewer agents) — no findings at confidence ≥8/10.
- Filter/reject paths both draw from the same
allowedGrantTypes/allowedResponseTypesmaps as DCR's stricter validators, so there's no drift and unsupported entries never reach the storedfosite.Client. authorization_code/coderemain hard-required, so the leniency is bounded.- Good regression coverage, including
TestFetch_VSCodeDocumentResolvesmirroring the real VS Code CIMD document from #6290.
Approving.
Summary
VS Code's real-world client-metadata document declares
urn:ietf:params:oauth:grant-type:device_codeingrant_typesalongsideauthorization_codeandrefresh_token, and the CIMD resolver rejected the entire document over it — surfacing to the user as an opaqueinvalid_client("The requested OAuth 2.0 Client does not exist") at/oauth/authorize. Since VS Code is the client CIMD was explicitly built for (#4825), this broke the feature for its primary consumer.The root problem is a semantic mismatch: a CIMD document describes the client's capabilities across every authorization server it talks to, and the client cannot tailor it per server — so an entry this server doesn't support must not be fatal. A DCR request, by contrast, is addressed to this server specifically, so strict rejection remains correct feedback there.
registration.FilterPublicGrantTypes/FilterPublicResponseTypesdrop unsupported entries instead of rejecting the set, but still reject when the intersection lacks the one flow this server offers (authorization_code/code) — such a client could never complete a token exchange, and a clear error at resolution beats failing every token requestbuildFositeClientrather than re-read from the raw documentValidatePublicGrantTypes/ValidatePublicResponseTypesare unchangedFixes #6290
Type of change
Test plan
task test)task lint-fix)New tests: a regression test resolving VS Code's real document shape (device_code alongside authorization_code, mixed loopback/https redirect URIs), filtered-vs-rejected cases in the decorator's grant/response-type tables, and table-driven tests for the two new filter functions. Pre-existing failures in
pkg/plugins/pluginsvcand a gosec finding incmd/thv/app/upgrade.goreproduce identically on cleanmainand are unrelated.Does this introduce a user-facing change?
MCP clients whose CIMD document declares grant types or response types the embedded auth server does not support (e.g. VS Code's
device_code) now resolve successfully, with the unsupported entries ignored, instead of failing withinvalid_client.Special notes for reviewers
FilterPublicGrantTypesgodoc./oauth/authorizeerror path logs nothing server-side, which made this hard to diagnose. The Debug log here covers the CIMD-rejection slice of that; the general authorize-path logging gap is left for a follow-up as the issue suggests.Generated with Claude Code