Skip to content

fix(api): preserve MCP OAuth credentials when updating server parameters - #45212

Open
wellparth wants to merge 1 commit into
langgenius:mainfrom
wellparth:fix/mcp-oauth-masked-client-id
Open

wellparth wants to merge 1 commit into
langgenius:mainfrom
wellparth:fix/mcp-oauth-masked-client-id

Conversation

@wellparth

Copy link
Copy Markdown

Fixes #45210

Summary

When modifying an existing MCP Server's configuration (such as server name, identifier, icon, timeout, or headers), the frontend submits the masked client_id (e.g., ba****c2) and client_secret (******) in the authentication payload.

Prior to this change:

  1. _merge_credentials_with_masked in MCPToolManageService looked for client_id and client_secret at the top level of existing_decrypted, where they are absent because credentials are saved under nested client_information (with secrets encrypted). As a result, the lookup failed and returned the masked placeholder string.
  2. _process_credentials unconditionally called _build_and_encrypt_credentials, which rewrote encrypted_credentials to a minimal dictionary with is_dynamic_registration: False, overwriting the real UUID with the masked placeholder (ba****c2), discarding dynamic client registration metadata, and erasing active OAuth tokens.
  3. Subsequent OAuth requests or re-authorizations failed against Keycloak / OAuth authorization servers with "Client not found".

Changes Made:

  • services/tools/mcp_tools_manage_service.py:
    • Fixed _merge_credentials_with_masked to extract existing unmasked credentials from mcp_provider_entity.retrieve_client_information() (or fallback to top-level if present), correctly restoring unmasked client_id and decrypted client_secret when masked placeholders are submitted.
    • Updated _process_credentials to detect when credentials have not changed and preserve existing mcp_provider.encrypted_credentials intact (maintaining active access/refresh tokens, DCR metadata, and dynamic registration flags).
    • Updated update_provider to preserve authed status and tools when credentials are unchanged, and reset authed = False with tools = EMPTY_TOOLS_JSON only when credentials actually change.
  • core/mcp/auth/auth_flow.py:
    • Added self-healing recovery: if client_information contains asterisks in client_id (from past corrupted edits) and authorization code exchange is not in progress (authorization_code is None), dynamically re-registers the client with the OAuth server to acquire and persist a fresh valid client ID.
    • Added rejection check to prevent attempting code exchange with a corrupted client ID.
  • Unit Tests:
    • Added 5 new unit tests in tests/unit_tests/services/tools/test_mcp_tools_manage_service.py verifying masked credentials restoration, unchanged credentials preservation, parameter updates without breaking active OAuth credentials/tools, and credential change resets.
    • Added 2 new unit tests in tests/unit_tests/core/mcp/auth/test_auth_flow.py verifying self-healing of corrupted masked client IDs and code exchange rejection.

Checklist

  • This change requires a documentation update, included: Dify Document
  • I understand that this PR may be closed in case there was no previous discussion or issues. (This doesn't apply to typos!)
  • I've verified the change and added or updated tests where meaningful regression risk justifies coverage.
  • I've updated the documentation accordingly.
  • I ran make lint && make type-check (backend) and vp staged (frontend) to appease the lint gods

@github-actions github-actions Bot added the size:L This PR changes 100-499 lines, ignoring generated files. label Oct 31, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:L This PR changes 100-499 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug] MCP OAuth re-authorization fails ("Client not found") after modifying server parameters due to masked client_id

1 participant