Summary
The embedded authorization server's CIMD support (#4825) rejects VS Code's real-world client-metadata document because it lists urn:ietf:params:oauth:grant-type:device_code in grant_types alongside authorization_code and refresh_token. The grant-types validator hard-rejects any document containing a grant type outside a fixed allowlist, instead of ignoring/filtering grant types the server doesn't need for the CIMD-driven flow. The end result is a generic invalid_client error that gives no indication of the real cause.
VS Code is the client this feature was explicitly designed for (#4825 background section), so this breaks CIMD for its primary intended consumer.
Reproduction
- Deploy a
VirtualMCPServer (or any embedded-AS-fronted MCP server) with authServerConfig.cimd.enabled: true and an upstream OIDC provider (reproduced against Entra ID, but upstream provider is irrelevant).
- Connect from VS Code (tested: 1.133.0) using its MCP client, which presents
client_id=https://vscode.dev/oauth/client-metadata.json.
- VS Code's document (fetched directly, confirmed reachable and well-formed):
{
"client_name": "Visual Studio Code",
"grant_types": ["authorization_code", "refresh_token", "urn:ietf:params:oauth:grant-type:device_code"],
"response_types": ["code"],
"token_endpoint_auth_method": "none",
"application_type": "native",
"client_id": "https://vscode.dev/oauth/client-metadata.json",
"client_uri": "https://vscode.dev/product",
"redirect_uris": ["http://127.0.0.1:33418/", "https://vscode.dev/redirect"]
}
- The
/oauth/authorize request fails, and the browser is shown:
{"error":"invalid_client","error_description":"Client authentication failed (e.g., unknown client, no client authentication included, or unsupported authentication method). The requested OAuth 2.0 Client does not exist."}
Other MCP clients that don't declare device_code in their CIMD/DCR metadata (e.g. Cursor) authenticate successfully against the same server.
Root cause
pkg/authserver/server/registration/dcr.go hardcodes the grant-types allowlist shared by both DCR and CIMD validation:
var allowedGrantTypes = map[string]bool{
"authorization_code": true,
"refresh_token": true,
}
validateGrantTypes rejects the entire client if any declared grant type isn't in this map. pkg/authserver/storage/cimd_decorator.go calls this same validator (registration.ValidatePublicGrantTypes) when resolving a CIMD document, so a document that declares an extra grant type the client doesn't intend to use for this flow still fails the whole resolution, surfacing as fosite.ErrInvalidClient at the authorize/token endpoints.
Separately, pkg/authserver/server/handlers/authorize.go's error path has no logging for this failure — kubectl logs shows nothing, making this hard to diagnose from the server side. (Worth a follow-up issue if not already tracked, pkg/authserver/server/handlers/token.go does log the analogous case.)
Suggested fix
For CIMD specifically, filter/ignore grant types the server doesn't recognize when constructing the fosite.Client, rather than rejecting the whole document — a client is free to declare general capabilities (device code, for other flows/products) that aren't relevant to the authorization_code+PKCE flow being used here. At minimum, log the specific validation failure reason server-side so this doesn't present as an opaque "client does not exist."
Environment
- ToolHive, tag
v0.42.1
- VS Code 1.133.0
Summary
The embedded authorization server's CIMD support (#4825) rejects VS Code's real-world client-metadata document because it lists
urn:ietf:params:oauth:grant-type:device_codeingrant_typesalongsideauthorization_codeandrefresh_token. The grant-types validator hard-rejects any document containing a grant type outside a fixed allowlist, instead of ignoring/filtering grant types the server doesn't need for the CIMD-driven flow. The end result is a genericinvalid_clienterror that gives no indication of the real cause.VS Code is the client this feature was explicitly designed for (#4825 background section), so this breaks CIMD for its primary intended consumer.
Reproduction
VirtualMCPServer(or any embedded-AS-fronted MCP server) withauthServerConfig.cimd.enabled: trueand an upstream OIDC provider (reproduced against Entra ID, but upstream provider is irrelevant).client_id=https://vscode.dev/oauth/client-metadata.json.{ "client_name": "Visual Studio Code", "grant_types": ["authorization_code", "refresh_token", "urn:ietf:params:oauth:grant-type:device_code"], "response_types": ["code"], "token_endpoint_auth_method": "none", "application_type": "native", "client_id": "https://vscode.dev/oauth/client-metadata.json", "client_uri": "https://vscode.dev/product", "redirect_uris": ["http://127.0.0.1:33418/", "https://vscode.dev/redirect"] }/oauth/authorizerequest fails, and the browser is shown:{"error":"invalid_client","error_description":"Client authentication failed (e.g., unknown client, no client authentication included, or unsupported authentication method). The requested OAuth 2.0 Client does not exist."}device_codein their CIMD/DCR metadata (e.g. Cursor) authenticate successfully against the same server.Root cause
pkg/authserver/server/registration/dcr.gohardcodes the grant-types allowlist shared by both DCR and CIMD validation:validateGrantTypesrejects the entire client if any declared grant type isn't in this map.pkg/authserver/storage/cimd_decorator.gocalls this same validator (registration.ValidatePublicGrantTypes) when resolving a CIMD document, so a document that declares an extra grant type the client doesn't intend to use for this flow still fails the whole resolution, surfacing asfosite.ErrInvalidClientat the authorize/token endpoints.Separately,
pkg/authserver/server/handlers/authorize.go's error path has no logging for this failure —kubectl logsshows nothing, making this hard to diagnose from the server side. (Worth a follow-up issue if not already tracked,pkg/authserver/server/handlers/token.godoes log the analogous case.)Suggested fix
For CIMD specifically, filter/ignore grant types the server doesn't recognize when constructing the
fosite.Client, rather than rejecting the whole document — a client is free to declare general capabilities (device code, for other flows/products) that aren't relevant to the authorization_code+PKCE flow being used here. At minimum, log the specific validation failure reason server-side so this doesn't present as an opaque "client does not exist."Environment
v0.42.1