From b71c5cf0fa27ce9da75dfd58069476545a932a32 Mon Sep 17 00:00:00 2001 From: Bobby Ho Date: Wed, 9 Sep 2026 12:48:17 -0700 Subject: [PATCH 1/3] docs(admin/integrations): note the OAuth2 registered scope enforcement for upgrades --- docs/admin/integrations/oauth2-provider.md | 24 ++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/docs/admin/integrations/oauth2-provider.md b/docs/admin/integrations/oauth2-provider.md index 7998d58b4e9..1c869c2865a 100644 --- a/docs/admin/integrations/oauth2-provider.md +++ b/docs/admin/integrations/oauth2-provider.md @@ -649,6 +649,30 @@ enforced, and such a request answers HTTP 400 with `error=invalid_scope`. The refresh token is not consumed, so a client that drops the parameter or asks for less recovers without re-authorizing. +Checking the `scope` an application registered at +[Dynamic Client Registration](#dynamic-client-registration) only affects +deployments running the `oauth2` experiment flag with that setting turned on. +Dynamic Client Registration is disabled by default, so if you never enabled it, +nothing changes for you. Earlier versions of Coder accepted any `scope` value at +registration without checking it, and every token for that application had +full access anyway. Coder now treats the registered `scope` as the list of +scopes the application is allowed to request, as described under +[Scopes](#scopes). Applications that registered without a `scope` are not +affected and continue to receive full access. If your application registered +with a scope name that this deployment does not offer, requests for that name +now fail with `invalid_scope`. If none of the registered names are offered, +every authorization for the application fails, even one that leaves `scope` +out. Authorization codes issued before the upgrade fail at the token endpoint +with `invalid_grant`. Applications created through the web UI or the management +API are not affected either, because they have no scope list. To fix an +affected application, update its registration with +`PUT /oauth2/clients/{client_id}`, using the `registration_access_token` +returned when it registered, so that `scope` lists only names from +`scopes_supported` in `GET /.well-known/oauth-authorization-server`. If you no +longer have that token, register the application again. Coder administrators +cannot change an application's registered `scope` from the web UI or the +management API. + ## Standards Compliance This implementation follows established OAuth2 standards including From 8537c5cd2c1481da067d90979a7f8f2e8119f1ba Mon Sep 17 00:00:00 2001 From: Bobby Ho Date: Thu, 10 Sep 2026 11:31:09 -0700 Subject: [PATCH 2/3] docs(docs/admin/integrations): cover clients registered before DCR was turned off The upgrade note said registered scope enforcement only affects deployments with Dynamic Client Registration turned on. The setting gates registration only. The stored scope of an existing application is checked on every authorization regardless, so a client registered while DCR was on remains affected after it is turned off. Refs PLAT-482 --- docs/admin/integrations/oauth2-provider.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/docs/admin/integrations/oauth2-provider.md b/docs/admin/integrations/oauth2-provider.md index cc1d57a16fb..16ed319412f 100644 --- a/docs/admin/integrations/oauth2-provider.md +++ b/docs/admin/integrations/oauth2-provider.md @@ -654,8 +654,12 @@ refresh token is not consumed, so a client that drops the parameter or asks for less recovers without re-authorizing. Checking the `scope` an application registered at -[Dynamic Client Registration](#dynamic-client-registration) only affects -deployments running the `oauth2` experiment flag with that setting turned on. +[Dynamic Client Registration](#dynamic-client-registration) affects any +application that registered with a `scope` while that setting was on, on a +deployment running the `oauth2` experiment flag. Turning Dynamic Client +Registration off only blocks new registrations. Coder checks the stored `scope` +of an existing application whether or not registration is still allowed, so an +application registered before the setting was turned off is affected too. Dynamic Client Registration is disabled by default, so if you never enabled it, nothing changes for you. Earlier versions of Coder accepted any `scope` value at registration without checking it, and every token for that application had From eeca6e1303af1af9e29db9e9c1b251118b4d24ea Mon Sep 17 00:00:00 2001 From: Bobby Ho Date: Fri, 11 Sep 2026 07:57:06 -0700 Subject: [PATCH 3/3] Update docs/admin/integrations/oauth2-provider.md Co-authored-by: Nick Vigilante --- docs/admin/integrations/oauth2-provider.md | 47 +++++++++------------- 1 file changed, 20 insertions(+), 27 deletions(-) diff --git a/docs/admin/integrations/oauth2-provider.md b/docs/admin/integrations/oauth2-provider.md index 16ed319412f..66f7c996e1f 100644 --- a/docs/admin/integrations/oauth2-provider.md +++ b/docs/admin/integrations/oauth2-provider.md @@ -653,33 +653,26 @@ enforced, and such a request answers HTTP 400 with `error=invalid_scope`. The refresh token is not consumed, so a client that drops the parameter or asks for less recovers without re-authorizing. -Checking the `scope` an application registered at -[Dynamic Client Registration](#dynamic-client-registration) affects any -application that registered with a `scope` while that setting was on, on a -deployment running the `oauth2` experiment flag. Turning Dynamic Client -Registration off only blocks new registrations. Coder checks the stored `scope` -of an existing application whether or not registration is still allowed, so an -application registered before the setting was turned off is affected too. -Dynamic Client Registration is disabled by default, so if you never enabled it, -nothing changes for you. Earlier versions of Coder accepted any `scope` value at -registration without checking it, and every token for that application had -full access anyway. Coder now treats the registered `scope` as the list of -scopes the application is allowed to request, as described under -[Scopes](#scopes). Applications that registered without a `scope` are not -affected and continue to receive full access. If your application registered -with a scope name that this deployment does not offer, requests for that name -now fail with `invalid_scope`. If none of the registered names are offered, -every authorization for the application fails, even one that leaves `scope` -out. Authorization codes issued before the upgrade fail at the token endpoint -with `invalid_grant`. Applications created through the web UI or the management -API are not affected either, because they have no scope list. To fix an -affected application, update its registration with -`PUT /oauth2/clients/{client_id}`, using the `registration_access_token` -returned when it registered, so that `scope` lists only names from -`scopes_supported` in `GET /.well-known/oauth-authorization-server`. If you no -longer have that token, register the application again. Coder administrators -cannot change an application's registered `scope` from the web UI or the -management API. +Coder now enforces the `scope` an application declared for itself when it self-registered through [Dynamic Client Registration](#dynamic-client-registration). +This affects only deployments that enabled Dynamic Client Registration and have an application that self-registered with a `scope`. +Dynamic Client Registration is disabled by default, so if you never enabled it, nothing changes for you. +Turning it back off does not clear the check: Coder validates the stored `scope` of an existing application whether or not registration is still allowed, so an application that self-registered before you turned the setting off is affected too. + +Earlier versions of Coder accepted any `scope` at registration without checking it, and every token for that application had full access. +Coder now treats the registered `scope` as the list of scopes the application is allowed to request, as described under [Scopes](#scopes). +Applications that self-registered without a `scope`, and applications created through the web UI or the management API, have no scope list and are not affected; they continue to receive full access. + +An affected application fails in the following ways: + +- A request for a scope name this deployment does not offer fails with `invalid_scope`. +- If none of the registered names are offered, every authorization fails, even one that leaves `scope` out. +- Authorization codes issued before the upgrade fail at the token endpoint with `invalid_grant` until they expire. + +For the full error details, refer to ["invalid_scope" returned to your callback](#invalid_scope-returned-to-your-callback) and ["invalid_grant" for a scope the deployment cannot mint](#invalid_grant-for-a-scope-the-deployment-cannot-mint). + +To fix an affected application, the party that holds its `registration_access_token` updates the registration with `PUT /oauth2/clients/{client_id}`, so that `scope` lists only names from `scopes_supported` in `GET /.well-known/oauth-authorization-server`. +If that token is lost, register the application again. +A Coder administrator cannot change an application's registered `scope` from the web UI or the management API; only the self-registration path writes that value. ## Standards Compliance