Tool Provider Authorization#
Dify's tool provider authorization system controls how credentials are stored, fetched, and displayed for tools used in Agent and Workflow surfaces. The key architectural split is between builtin providers (plugin-based, managed via the plugin daemon with credentials stored in BuiltinToolProvider) and non-builtin providers (api, workflow, mcp, plugin) which use separate storage and different API routes. Routing the wrong provider type to a builtin API endpoint causes PluginNotFoundError (HTTP 500) β the root cause of issue #39169.
Provider types are enforced as a 7-value enum (builtin, api, plugin, workflow, mcp, app, dataset-retrieval) end-to-end as of PR #39380 .
Frontend UI Gating#
Workflow Tool Node β Canvas Warning#
The canvas tool node (node.tsx) shows an "Authorization Required" warning badge when isToolAuthorizationRequired() returns true. The function requires all three conditions:
providerType === CollectionType.builtIncollection.allow_delete === truecollection.is_team_authorization === false
Only builtin providers can trigger this warning. Non-builtin types (api, mcp, workflow) never show the badge .
Workflow Tool Node β Side Panel#
In the side panel, isShowAuthBtn (which gates the parameter form vs. auth prompt) uses the same isToolAuthorizationRequired() logic . When false, the tool input form is rendered normally; when true, the auth prompt replaces it.
Agent Tool Selector β Auth Section#
In the agent tool detail panel, ToolAuthorizationSection renders only when currentProvider.type === CollectionType.builtIn && currentProvider.allow_delete. For all other provider types, it returns null. When it does render, it mounts PluginAuthInAgent, which calls usePluginAuth and renders either an Authorize or Authorized credential picker. The picker always includes a "Workspace Default" synthetic entry (__workspace_default__) alongside per-user credentials .
Credential Schema Routing#
useGetApi Hook#
All plugin-auth API URLs are resolved by useGetApi, which branches on category from PluginPayload:
AuthCategory.toolβ URLs point to/workspaces/current/tool-provider/builtin/<provider>/...AuthCategory.datasourceβ URLs point to/auth/plugin/datasource/<provider>/...- All other categories β returns an all-empty-string object that silently prevents requests
For builtin tools, the key credential endpoints are:
| Purpose | Endpoint |
|---|---|
| Credential info | GET .../credential/info |
| Credential schema | GET .../credential/schema/<type> |
| OAuth client schema | GET .../oauth/client-schema |
| CRUD | POST .../add, .../update, .../delete |
enabled Gating Pattern#
Every useQuery call in the plugin-auth hooks checks enabled: !!url. When useGetApi returns an empty string for a given URL, that query is silently disabled β no request fires, no error surfaces.
Bug & Fix: Non-Builtin Providers Hitting Builtin Routes#
Issue #39169: On the Agents configure page, custom API tool providers triggered requests to GET /tool-provider/builtin/<id>/credential/info. Since custom API providers have no plugin daemon entry, the backend raised PluginNotFoundError β HTTP 500 on every page load .
PR #39206 applied two layers of defense :
-
Hook-level (
use-get-api.ts): Whencategory === AuthCategory.tool,providerTypeis provided, andproviderType !== CollectionType.builtIn, the hook now returnsEMPTY_API(all empty-string URLs). Theenabled: !!urlgate then prevents all requests. Callers that omitproviderType(legacy sites) still receive builtin URLs for backward compatibility. -
Component-level (
CredentialStatusinitem.tsx): AnisBuiltinProviderguard returnsnullbeforeusePluginAuthis even invoked for non-builtin providers β preventing both the API call and the misleading "Not Authorized" UI.
Type Safety Enforcement β PR #39380#
PR #39380 replaced loose string-typed provider_type fields with a strict enum enforced at every boundary :
- Backend:
AgentSoulDifyToolConfig.provider_typechanged from optionalstr = "plugin"to requiredToolProviderTypePydantic enum - Generated contracts:
packages/contracts/generated/api/console/agent/types.gen.tsexportsToolProviderTypeand a Zod validatorzToolProviderType - Frontend: A
CollectionProviderTypeunion combinesCollectionType,DatasourceProviderType, andToolProviderTypefor type-safe cache invalidation and URL routing
A secondary fix in this PR removed an unreachable builtin branch in datasource authorization that had been silently masking incorrect auth state.
Permission Hint Gating β PR #39721#
PR #39721 (merged 2026-07-29) tightened the showPermissionHint condition in plugin-auth.tsx . The "no permission" hint now requires both unauthorized state and !canCreateCredential β preventing false positives for users who can actually create credentials.
Key Files#
| File | Role |
|---|---|
web/.../nodes/tool/auth.ts | isToolAuthorizationRequired() β builtin-only guard |
web/.../nodes/tool/node.tsx | Canvas: "Authorization Required" warning badge |
web/.../nodes/tool/panel.tsx | Side panel: gates param form via isShowAuthBtn |
web/.../tool-selector/components/tool-authorization-section.tsx | Agent detail: auth section guard (builtin-only) |
web/.../plugin-auth/plugin-auth-in-agent.tsx | Credential picker UI (authorize/authorized) |
web/.../plugin-auth/hooks/use-get-api.ts | Routes credential API URLs by category/providerType |
web/.../plugin-auth/types.ts | PluginPayload, AuthCategory, credential types |
web/.../plugin-auth/authorized/item.tsx | Credential list item (rename/edit/delete/select) |
api/services/tools/builtin_tools_manage_service.py | Backend: builtin credential CRUD, encryption, Redis cache |