Summary
The embedded auth server issues incoming access tokens with an empty aud claim when clients don't send the optional RFC 8707 resource parameter in the token request. This causes VirtualMCPServer to reject tokens issued by its own auth server.
Problem
We have two configurations using the embedded auth server:
MCPServer with MCPExternalAuthConfig (works): The MCPServer's oidcConfig.inline does not set audience, so the incoming auth validator skips the audience check when no expected audience is configured (pkg/auth/token.go:947). Incoming tokens with empty aud claims pass validation.
VirtualMCPServer with inline authServerConfig (fails): The VirtualMCPServer requires audience in incomingAuth.oidcConfig.inline. Without it, the server fails config validation on startup:
{"level":"ERROR","msg":"Configuration validation failed: invalid configuration:\n - incomingAuth.oidc.audience is required"}
With audience configured, the incoming auth validator checks the token's aud claim against the expected audience (pkg/auth/token.go:948-962) and rejects the token the auth server just issued, because the token's aud claim is empty.
Both use the same embedded auth server runtime and the same token handler (pkg/authserver/server/handlers/token.go). We confirmed by inspecting the VirtualMCPServer's issued incoming token in Redis that it has "Audience":[], "requested_audience":null, "granted_audience":[]. The MCPServer uses the same token handler, so it produces the same token shape. The auth server only populates the aud claim when the client sends a resource parameter (RFC 8707) in the token request. Since resource is optional per the spec, clients may not send it, and the token is issued without an audience even though AllowedAudiences is configured.
Proposed Fix
When no resource parameter is sent in the token request and AllowedAudiences contains exactly one entry, the auth server should default to granting that audience. The allowed audience is already derived from the server's resourceUrl, so the intended audience is unambiguous.
Reproduction
- Deploy a VirtualMCPServer with
authServerConfig and incomingAuth.oidcConfig.inline.audience set
- Connect via a client that does not send
resource in the token request
- OAuth flow completes — token exchange returns 200
- MCP request fails with 401 — incoming token has empty audience, validator expects the configured audience
Summary
The embedded auth server issues incoming access tokens with an empty
audclaim when clients don't send the optional RFC 8707resourceparameter in the token request. This causes VirtualMCPServer to reject tokens issued by its own auth server.Problem
We have two configurations using the embedded auth server:
MCPServer with MCPExternalAuthConfig (works): The MCPServer's
oidcConfig.inlinedoes not setaudience, so the incoming auth validator skips the audience check when no expected audience is configured (pkg/auth/token.go:947). Incoming tokens with emptyaudclaims pass validation.VirtualMCPServer with inline authServerConfig (fails): The VirtualMCPServer requires
audienceinincomingAuth.oidcConfig.inline. Without it, the server fails config validation on startup:With
audienceconfigured, the incoming auth validator checks the token'saudclaim against the expected audience (pkg/auth/token.go:948-962) and rejects the token the auth server just issued, because the token'saudclaim is empty.Both use the same embedded auth server runtime and the same token handler (
pkg/authserver/server/handlers/token.go). We confirmed by inspecting the VirtualMCPServer's issued incoming token in Redis that it has"Audience":[],"requested_audience":null,"granted_audience":[]. The MCPServer uses the same token handler, so it produces the same token shape. The auth server only populates theaudclaim when the client sends aresourceparameter (RFC 8707) in the token request. Sinceresourceis optional per the spec, clients may not send it, and the token is issued without an audience even thoughAllowedAudiencesis configured.Proposed Fix
When no
resourceparameter is sent in the token request andAllowedAudiencescontains exactly one entry, the auth server should default to granting that audience. The allowed audience is already derived from the server'sresourceUrl, so the intended audience is unambiguous.Reproduction
authServerConfigandincomingAuth.oidcConfig.inline.audiencesetresourcein the token request