feat(authserver): support additional token-request params (RFC 8707 resource indicators) - #6430
feat(authserver): support additional token-request params (RFC 8707 resource indicators)#6430aron-muon wants to merge 1 commit into
Conversation
Some authorization servers enforce RFC 8707 resource indicators on token requests as well as authorization requests: the code exchange and refresh are rejected with invalid_target unless the resource parameter is present in the POST form body (query-string placement is ignored). Nominal's MCP authorization server (api.gov.nominal.io) is a live example — with only additionalAuthorizationParams, the flow passes authorization and then fails at the code exchange. Add AdditionalTokenParams alongside AdditionalAuthorizationParams: - upstream.CommonOAuthConfig gains AdditionalTokenParams, applied in BaseOAuth2Provider.exchangeCodeForTokens and RefreshTokens via oauth2.SetAuthURLParam options (which land in the POST form body on Exchange). OIDC providers inherit both paths through the embedded base provider. - Reserved-parameter validation mirrors the authorization-side list with token-request semantics: grant_type, code, redirect_uri, client_id, client_secret, code_verifier, refresh_token, and scope are rejected. - CRD: additionalTokenParams on both oidcConfig and oauth2Config upstream provider types, plumbed through the operator run-config builders and validated at reconcile time (MCPExternalAuthConfig and VirtualMCPServer), matching the additionalAuthorizationParams treatment. - Regenerated deepcopy, CRD manifests, and CRD API docs. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: Aron Gates <aron@muonspace.com>
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #6430 +/- ##
==========================================
- Coverage 77.81% 77.81% -0.01%
==========================================
Files 760 760
Lines 73133 73160 +27
==========================================
+ Hits 56908 56926 +18
- Misses 16220 16229 +9
Partials 5 5 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
jhrozek
left a comment
There was a problem hiding this comment.
Sorry this took us so long to review, and thank you for the contribution — the RFC 8707 use case and the end-to-end token-form coverage are both very helpful.
blocker: Please reserve client_assertion and client_assertion_type in ReservedTokenParams. They are RFC 7523 client-authentication credentials; allowing them through additionalTokenParams can combine them with ToolHive's configured client_secret or HTTP Basic authentication, producing an invalid multi-method client-auth request. It also allows an assertion credential to live in CRD configuration instead of a secret-backed credential path.
suggestion: When resource is configured, validate it as an absolute URI with no fragment. RFC 8707 requires that shape, while the current name-only validation accepts an empty, relative, malformed, or fragment-bearing value that will fail later at the authorization server.
suggestion: Please add BuildAuthServerRunConfig tests that assert AdditionalTokenParams propagates for both OIDC and OAuth2 upstreams. The direct HTTP-form tests are good, but the new CRD-to-runtime mappings can otherwise regress silently; the adjacent AdditionalAuthorizationParams test cases are a natural template.
Problem
Some authorization servers enforce RFC 8707 resource indicators on token requests, not just authorization requests: the code exchange and refresh are rejected with
invalid_targetunlessresourceis present in the POST form body (query-string placement is ignored).additionalAuthorizationParamsonly reaches the authorize URL, so against such an AS the embedded auth server passes authorization and then fails at the code exchange. Live example: Nominal's MCP authorization server (api.gov.nominal.io) — probing its token endpoint with a bogus code returns{"error":"invalid_target"}withoutresourcein the body, and proceeds to code validation (reauthorization_required) with it.Change
Adds
AdditionalTokenParamsalongsideAdditionalAuthorizationParams:upstream.CommonOAuthConfiggainsAdditionalTokenParams, applied inBaseOAuth2Provider.exchangeCodeForTokensandRefreshTokensviaoauth2.SetAuthURLParamoptions, which land in the POST form body onExchange. OIDC providers inherit both paths through the embedded base provider.grant_type,code,redirect_uri,client_id,client_secret,code_verifier,refresh_token, andscopeare rejected.additionalTokenParamson bothoidcConfigandoauth2Configupstream provider types, plumbed through the operator run-config builders and validated at reconcile time (MCPExternalAuthConfigandVirtualMCPServer), matching theadditionalAuthorizationParamstreatment.Testing
additionalTokenParamscases for both provider types.go build ./...,go test ./pkg/authserver/... ./cmd/thv-operator/api/v1beta1/ ./cmd/thv-operator/pkg/controllerutil/ ./cmd/thv-operator/controllers/all pass.🤖 Generated with Claude Code