Skip to content

[BUG]: DCR creates new OAuth clients per session, triggering Entra ID admin consent on every use #2093

@Maxim-Mazurok

Description

@Maxim-Mazurok

Describe the bug

Copilot CLI's Dynamic Client Registration (DCR) creates new OAuth clients on every session, triggering Entra ID "admin approval required" prompts in enterprise environments where the IDP broker federates to Azure AD/Entra ID.

This is related to but distinct from #1491 (redirect_uri mismatch). #1491 covers the port mismatch causing OAuth failures. This issue covers the consequence of DCR creating new clients constantly: enterprise IdPs that federate authentication to Entra ID require admin consent for each new dynamically registered client, blocking non-admin users entirely.

What happens

  1. Copilot CLI connects to a remote MCP server requiring OAuth
  2. CLI performs DCR with the authorization server (e.g., Keycloak)
  3. A new OAuth client is registered (new ephemeral port → new redirect_uri → new client)
  4. Keycloak federates the authorization to Entra ID (Azure AD)
  5. Entra ID sees an unrecognized OAuth client and shows "Approval required — This app requires your admin's approval"
  6. Non-admin users cannot proceed — they can only submit a justification and wait for admin approval
  7. CLI times out after 5 minutes: MCPOAuthError: OAuth callback timeout

Why VS Code doesn't have this problem

VS Code's MCP OAuth implementation avoids this because (per #1491's analysis) it uses a preferred fixed port (33418) with ephemeral fallback. This means:

  • DCR'd clients are reused across sessions (same port → same redirect_uri → same client)
  • Entra ID admin consent is only needed once for the stable client
  • After initial consent, sessions work without browser interaction

Copilot CLI uses port 0 (OS-assigned ephemeral) every time, creating a new client on every session, requiring fresh admin consent each time.

Impact

  • Multiple users in our organization started hitting this simultaneously
  • Users see "Approval required" for the Keycloak IDP broker app in Entra ID
  • Non-admin users are completely blocked from using remote MCP servers
  • Automated workflows that invoke copilot --autopilot are also blocked since there's no human to interact with the consent page
  • Tokens expire every ~12 hours, so even if admin consent is granted for one client, the next session creates a new client requiring consent again

Affected version

GitHub Copilot CLI 1.0.6

Steps to reproduce the behavior

Prerequisites

  • Enterprise environment with Entra ID (Azure AD) for identity
  • Remote MCP server with OAuth, authorization server using Keycloak with IDP broker to Entra ID
  • Entra ID tenant with admin consent policy requiring approval for unrecognized apps

Steps

  1. Configure ~/.copilot/mcp-config.json:
{
  "mcpServers": {
    "my-server": {
      "url": "https://my-mcp-server.example.com",
      "type": "http"
    }
  }
}
  1. Clear any cached OAuth state: rm -rf ~/.copilot/mcp-oauth-config/
  2. Run: copilot
  3. CLI opens browser for OAuth — browser shows Entra ID "Approval required" page
  4. Non-admin users cannot approve; CLI times out after 5 minutes

If admin consent IS granted:
6. Session works until tokens expire (~12 hours)
7. On next copilot invocation, CLI creates a new DCR client (new ephemeral port)
8. Entra ID requires admin consent again for the new client
9. Back to step 4

Expected behavior

Copilot CLI should reuse OAuth client registrations across sessions to avoid triggering repeated admin consent prompts. Specifically:

  1. Use a preferred fixed port (like VS Code uses 33418) so that DCR'd clients are stable across sessions
  2. Reuse existing client registration when a cached one exists with the same server, instead of always creating new ones
  3. Support CIMD (Support CIMD for Remote OAuth MCP Servers #1305) as an alternative to DCR, which avoids per-session client creation

Additional context

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions