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 existing oauth_settings JSONB 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_endpoint when disabled, so compliant clients do not attempt registration.
    • POST /oauth/register returns 403 access_denied when disabled, so clients that ignore discovery (or use a stale cached copy) are still blocked.
  • Cleanup on disable: when the setting is turned off, ApplicationSettings::UpdateService enqueues Authn::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 existing auto_approve_blocked_users pattern, 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).

  1. 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"
  2. POST /oauth/register — expect 403 with {"error":"access_denied"}.

  3. GET /.well-known/oauth-authorization-server/api/v4/mcp — expect no registration_endpoint key.

  4. Confirm Authn::OauthApplications::CleanupDynamicApplicationsWorker was enqueued.

🤖 Generated with Claude Code

Edited by Jessie Young

Merge request reports

Loading