Add API setting to disable OAuth dynamic client registration
What does this MR do and why?
Adds an instance setting to disable OAuth dynamic client registration
(DCR, RFC 7591). When
disabled, OAuth clients cannot register applications automatically via
POST /oauth/register and must be pre-registered as OAuth applications.
The primary driver is MCP: dynamic client registration is mostly used by MCP clients (AI tools), and automatic registration can create OAuth application spam. This setting lets administrators keep MCP enabled while requiring pre-registration.
This is the backend/API-only portion of
!245457. The admin UI toggle (HAML form, JS warning,
.pot strings, view spec) is intentionally excluded — the setting is
configured through the application settings REST API instead. It also adds
the cleanup behavior that was designed as a follow-on to
!246028 (merged).
Implementation
- Setting
dynamic_client_registration_enabled(default:true) stored in the existingoauth_settingsJSONB column — no migration required. - Exposed and settable via the application settings REST API
(
dynamic_client_registration_enabled). - Enforcement (both gated on the instance setting):
- OAuth discovery documents omit
registration_endpointwhen disabled, so compliant clients do not attempt registration. POST /oauth/registerreturns403 access_deniedwhen disabled, so clients that ignore discovery (or use a stale cached copy) are still blocked.
- OAuth discovery documents omit
- Cleanup on disable: when the setting is turned off,
ApplicationSettings::UpdateServiceenqueuesAuthn::OauthApplications::CleanupDynamicApplicationsWorker(added in !246028 (merged)) to destroy the dynamically registered OAuth applications and revoke their tokens/grants. This is placed in the update service — the single choke point for both the REST API and admin controller — mirroring the existingauto_approve_blocked_userspattern, rather than in an ActiveRecord callback. - The setting is instance-wide. Because the default is
true, behavior is unchanged until an administrator turns it off via the API.
How to set up and validate locally
Must be running GDK in Self-Managed mode (non-SaaS mode).
-
Disable DCR via the API:
curl --request PUT --header "PRIVATE-TOKEN: <admin_token>" \ --url "https://gitlab.example.com/api/v4/application/settings?dynamic_client_registration_enabled=false" -
POST /oauth/register— expect403with{"error":"access_denied"}. -
GET /.well-known/oauth-authorization-server/api/v4/mcp— expect noregistration_endpointkey. -
Confirm
Authn::OauthApplications::CleanupDynamicApplicationsWorkerwas enqueued.
Related
- Resolves #601438
- Backend-only split of !245457
- Enqueues the worker from !246028 (merged)