feat(mcp): support OAuth for remote OIDC clients - #1920
mfroembgen wants to merge 3 commits into
Conversation
PR Summary by QodoAdd OAuth authorization for remote MCP clients in OIDC deployments
AI Description
Diagram
High-Level Assessment
Files changed (17)
|
Code Review by Qodo
1.
|
| mcpError(w, 401, "invalid_token") | ||
| return | ||
| } | ||
| challenge := `Bearer resource_metadata="` + s.issuer + "/.well-known/oauth-protected-resource" + r.URL.Path + `", scope="mcp"` |
There was a problem hiding this comment.
2. Subpath clients cannot discover oauth 🐞 Bug ≡ Correctness
MCPOAuthServer.Authenticate constructs the resource_metadata challenge URL by appending /.well-known/oauth-protected-resource after s.issuer, even though s.issuer already includes the configured base path. For a deployment under /radar, clients that follow the advertised https://host/radar/.well-known/oauth-protected-resource/mcp URL receive no protected-resource metadata because the server mounts it at https://host/.well-known/oauth-protected-resource/radar/mcp, preventing the OAuth flow from starting.
Agent Prompt
Issue description
`Authenticate` advertises protected-resource metadata below the issuer path, while the router correctly mounts RFC 9728 metadata below the origin with the base path appended after `/.well-known/oauth-protected-resource`. This breaks the `WWW-Authenticate` discovery URL for every non-root `basePath` deployment.
Fix Focus Areas
- internal/auth/mcp_oauth.go[474-474]
- internal/server/mcp_oauth.go[25-33]
Recommended Fix
Construct the `resource_metadata` challenge URL from `s.origin`, followed by `/.well-known/oauth-protected-resource`, `s.basePath`, and the requested MCP path. Keep it identical to the route registered by `mountMCPOAuthMetadata`; for example, a `/radar/mcp` resource must advertise `https://host/.well-known/oauth-protected-resource/radar/mcp`.
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Want higher recall? High effort reviews run extra passes and find more bugs. A team admin can switch effort levels in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 2325093. Configure here.

Description
Remote MCP clients receive a 401 from an OIDC-protected Radar but cannot discover an authorization server or refresh credentials. This adds opt-in MCP OAuth through
--mcp-oauth/mcp.oauth.enabled, allowing clients to use the existing browser login, approve access, and obtain their own credentials.The implementation includes protected-resource and authorization-server discovery, dynamic public-client registration, explicit CSRF-protected consent, mandatory S256 PKCE, one-time authorization codes, short-lived access tokens, and rotating refresh tokens. Tokens are bound to the client and exact MCP endpoint;
/mcp-readonlycredentials cannot access/mcpor the REST API. User/group identity continues through the existing Kubernetes RBAC and impersonation paths. Browser and provider logout revoke MCP grants, including pending approvals.OAuth state is bounded and held in memory. This initial implementation requires standalone OIDC mode and one replica; the chart rejects incompatible configurations and uses Recreate rollouts. Restarts require client re-registration and authorization. Access tokens last 10 minutes, with an absolute 24-hour authorization lifetime. Anonymous registration is rate-limited, and unapproved registrations expire after 10 minutes. Documentation covers setup, ingress discovery, protocol requirements for compatible MCP clients, and these lifecycle limits.
Type of change
How has this been tested?
go test ./...go test -race ./internal/auth ./internal/serverhelm lint deploy/helm/radar, chart render/schema checks, andmake test-chart.Interoperability is verified with the official MCP Go SDK. Individual client applications and deployment against a Kubernetes cluster have not been exercised for this change.
Checklist
Related issues
Fixes #1434
Note
High Risk
Introduces a new OAuth authorization server, token issuance, and MCP authentication path on top of OIDC—security-critical auth surface with in-memory state and single-replica deployment assumptions.
Overview
Adds opt-in MCP OAuth (
--mcp-oauth, Helmmcp.oauth.enabled) so remote MCP clients on standalone OIDC deployments can authenticate without copying browser session cookies. Radar acts as the MCP authorization server: discovery (/.well-known/*), dynamic public-client registration, browser consent after existing OIDC login, authorization code + S256 PKCE, and short-lived access tokens with rotating refresh tokens bound to/mcpor/mcp-readonlyonly (not the REST API).Wiring and constraints: startup and the chart fail fast unless MCP is on,
auth.mode=oidc, a single replica, and not Cloud/tunnel/catalog modes. OAuth state is in-memory, so the chart uses Recreate rollouts and documents re-register/re-authorize after restarts. Browser logout, OIDC backchannel logout, and a signedreturn_tocontinuation after IdP login tie MCP grants to browser sessions.Docs, values/schema, and Helm unittest cases cover setup, ingress paths, token lifetimes, and limits.
Reviewed by Cursor Bugbot for commit f9bf36c. Bugbot is set up for automated code reviews on this repo. Configure here.