feat: gate the OAuth2 provider on CODER_OAUTH2_PROVIDER_ENABLE - #29251
feat: gate the OAuth2 provider on CODER_OAUTH2_PROVIDER_ENABLE#29251BobbyHo wants to merge 4 commits into
Conversation
Move every OAuth2 provider gate off the oauth2 experiment and onto the deployment flag added in the previous commit. The flag is off by default, so a deployment that relied on CODER_EXPERIMENTS=oauth2 must set it or its OAuth2 clients receive 404 responses after upgrading. - Add httpmw.RequireOAuth2Provider, which answers 404 while the provider is disabled. A disabled provider looks like a deployment that never had one. There is no bypass for development builds. - Use it on both discovery documents, the /oauth2 tree, the admin apps API, and the MCP HTTP endpoint, which keeps its mcp-server-http experiment check. The AI Gateway's deprecated MCP injection reads the flag too. - Keep /api/v2/oauth2-provider/settings reachable while disabled so an admin can configure dynamic client registration before enabling the provider. - Gate both OAuth2 navigation items on the new build info field instead of the experiment, and drop the development-build escape hatch. The oauth2 experiment is still parsed and reported; a follow-up makes it a no-op with a specific warning. Part of PLAT-492.
6b99d50 to
cf2e665
Compare
|
/coder-agents-review |
|
Chat: Review in progress (18/18 reviewers complete) | View chat deep-review v0.9.0 | Round 1 | Last posted: Round 1, 7 findings (1 P2, 4 P3, 1 Nit, 1 Note), COMMENT. Review Finding inventoryFinding inventory: PR #29251Findings
Round logRound 1Netero clean (one Note). Panel of 17 (bisky, hisoka, mafu-san, mafuuu, pariston, komugi, gon, leorio, kurapika, knov, kite, ryosuke, chopper, nami, ging-go + wildcards meruem, zoro). Security perimeter verified complete: every prior RequireExperimentWithDevBypass(ExperimentOAuth2) gate converted, no production route left ungated, disabled-provider 404 indistinguishability confirmed by Kurapika/Knov/Hisoka. 1 P2, 4 P3, 1 Nit, 1 Note. Reviewed against 6ad05a6..5334b19. About deep-reviewCRF = Coder Review Finding (P0-P4, Nit, Note)
|
There was a problem hiding this comment.
This is a clean, well-scoped security migration. Every prior RequireExperimentWithDevBypass(ExperimentOAuth2) gate now reads the deployment flag through one small RequireOAuth2Provider middleware, no production route was left ungated (grep confirms only the experiment definition survives, as the stack intends), and the disabled provider returns the exact route-not-found body so it is indistinguishable from a deployment that never had one, which is what the RFC 8414/9728 discovery clients expect. The negative path is directly tested: TestOAuth2ProviderDisabled asserts 404 on all eight gated routes with the flag off and not-404 with it on, so a typo in the gated-route list fails the enabled arm rather than passing vacuously. The incidental removal of the dev bypass on the MCP HTTP experiment is called out and pinned by tests. Kurapika, Knov, and Hisoka each probed the perimeter independently and found it complete. As Hisoka put it: "Boring, in the way a locked door is boring."
Counts: 1 P2, 4 P3, 1 Nit, 1 Note. No P0/P1.
The P2 is a cross-cutting doc break outside the diff: the canonical setup docs still tell admins to enable the oauth2 experiment, which this PR makes ineffective for the gate. The PR schedules a docs PR later in the stack but links no ticket for these specific pages, and the "do not release main until the experiment no-op PR merges" warning covers the experiment PR, not docs. That deferral needs a human decision: file a tracking ticket for the stale pages or state explicitly that the PLAT-492 stack tracking is sufficient.
The four P3s are all comment/coverage accuracy, not behavior: the frontend gating behavior has no mechanical assertion (only screenshots), one load-bearing comment annotates the wrong route, and the func() bool comment promises a runtime toggle the two sibling readers cannot honor. Each is cheap to fix and worth fixing now, since the author-agent will not revisit this code.
docs/admin/integrations/oauth2-provider.md:25
P2 [CRF-5] The canonical "Enable OAuth2 Provider" docs still tell admins to set the oauth2 experiment, which this PR makes ineffective for the gate. (Mafuuu)
after this PR every gate reads
CODER_OAUTH2_PROVIDER_ENABLE[...] Setting only the experiment leaves the flag off, soRequireOAuth2Providerreturns 404 for the whole/oauth2tree, both discovery documents, and/api/v2/oauth2-provider/apps.
Confirmed: docs/admin/integrations/oauth2-provider.md lines 11, 25-34, and 401 all instruct coder server --experiments oauth2 / CODER_EXPERIMENTS=oauth2, and docs/ai-coder/mcp-server.md:186,238 repeat it. An admin who follows the documented path to turn on the provider gets 404 for every OAuth2 client, the exact breaking-upgrade failure the PR calls out, delivered to anyone reading the docs. The PR defers docs to a later PR in the stack but links no ticket for these specific pages, and the stack's release-gate warning covers the experiment no-op PR, not docs, so the pages stay wrong on main across PRs 3 and 4. This needs a human decision: file a tracking ticket for the stale pages, or state explicitly that the PLAT-492 epic tracking is sufficient. Root cause is one class: every doc reference to the oauth2 experiment as the enable mechanism; fix them together.
🤖
site/src/router.tsx:649
Note [CRF-7] With the flag off, the OAuth2 pages stay routable by direct URL and render an ErrorAlert from the 404, not a not-found page. (Nami)
The routes at
/settings/oauth2-providerand/deployment/oauth2-provider/appsare registered unconditionally. When the flag is off, the nav items are hidden but a bookmarked or typed URL still mounts the page, which fetches/api/v2/oauth2-provider/apps, gets the 404, and showsErrorAlert(degraded, not blank).
Not introduced by this PR: the API was gated before via the experiment middleware, so the same URL-reaches-disabled-page-then-errors class already existed, and behavior stays consistent with the new gate. Flagging so a human can decide whether a disabled provider should redirect to a not-found page rather than surface the raw 404; hiding the nav does not gate the route.
🤖
🤖 This review was automatically generated with Coder Agents.
|
Re the P2 in review 5183941183 (CRF-5, stale PR 5 of this stack owns the docs. It rewrites both pages plus |
RequireOAuth2Provider takes a bool: the flag is read once at startup in three places, so the closure promised a runtime toggle that would ship half-working. The settings comment moves onto the route it describes, and both enabled sidebar stories set the flag explicitly instead of relying on the fixture default.
The MCP HTTP endpoint no longer has a development-build bypass for the mcp-server-http experiment, so tests that reach it must opt in.
TL;DR
Second of five for PLAT-492. Every OAuth2 provider gate now reads
CODER_OAUTH2_PROVIDER_ENABLEinstead of theoauth2experiment. The flag is off by default, so this is the breaking step: a deployment that relied onCODER_EXPERIMENTS=oauth2must set the flag or its OAuth2 clients get 404 responses after upgrading.oauth2experiment a no-op with a startup warning.release/breaking.Implementation Details
The gate
httpmw.RequireOAuth2Provideranswers 404 with the standard route-not-found body while the provider is disabled. A disabled provider is indistinguishable from a deployment that never had one, which is what RFC 8414 and RFC 9728 discovery clients expect. There is no RFC 6749 error code for "no authorization server here", and inventing one would confirm there could be.develbuilds through; a GA control must not. Tests get the provider through thecoderdtestdefault from feat: add the CODER_OAUTH2_PROVIDER_ENABLE deployment flag #29248.func() boolread through the deployment values on every request, so a runtime toggle can be added later without touching call sites.Where it applies
/oauth2tree, and the admin apps API under/api/v2/oauth2-provider/apps./api/experimental/mcp/httpneeds the flag and still needs themcp-server-httpexperiment. That experiment check no longer has a dev bypass either; it only had one because it shared the OAuth2 call.Settings stay reachable
GET/PUT /api/v2/oauth2-provider/settingswork while the provider is disabled, so an admin can turn on dynamic client registration before enabling the provider. The gate moved one level down onto/apps. The setting has no effect until the flag is set.Dashboard
buildInfo.oauth2_provider. Theexperiments.includes("oauth2")check and thedevelescape hatch are gone. The admin sidebar no longer needs itsexperimentsprop.Follow-ups this PR leaves alone
oauth2experiment is still parsed and listed by/api/v2/experiments. The next PR turns it into a no-op with a warning naming the flag. Until it merges,mainshould not be released.Tests
TestRequireOAuth2Providercovers the middleware on its own: 404 and no call to the next handler when disabled, pass-through when enabled.TestOAuth2ProviderDisabledrequests all eight gated routes with the flag off and asserts 404 with the exact route-not-found body, then repeats with the flag on and asserts none is 404. It also checks the settings endpoints answer with the flag off, and that the MCP endpoint still returns the experiment's 403 when only the flag is set.TestOAuth2ProviderSettingsAuditDiffnow runs with the provider disabled, which proves the settings write is still audited in that state.develversion so the screenshot shows the build type no longer matters.DeploymentValuesfixture needed changing.