快速结论:当 Dify 自托管实例使用支持 OAuth2 订阅的 Trigger 插件,且订阅凭据结构中包含 access_token、refresh_token 等 secret-input 字段时,触发 OAuth token 刷新会出现“加密/解密所用 schema 不一致”的问题,即 Trigger OAuth token refresh encrypts/decrypts subscription credentials with the OAuth client schema。核心排查点是 refresh_oauth_token 构造 ProviderConfigEncrypter 时使用的是 OAuth client schema,而不是订阅凭据的 credentials schema。
适用环境:自托管源码部署(Self Hosted / Source)的 Dify;Issue 所述问题在 main 分支(源码分析位置 141cbf9832)及 1.16.1 中存在。Issue 未确认操作系统、Python、CUDA、显卡等环境信息,不要据此推断。
最快修复方案:暂无确认的一步修复方案。Issue 中提出的修复方向(#43593)是让 refresh_oauth_token 复用 create_trigger_provider_encrypter_for_subscription,使其与 add / update / list / get / refresh_subscription 使用同一套 credentials schema 进行加解密。
注意事项:Issue 正文说明该问题来自源码阅读,尚未在运行实例上复现;修复 PR 的合入状态与发布版本需以仓库实际记录为准。替换 encrypter 时还要核对缓存差异:refresh_oauth_token 当前使用 NoOpProviderCredentialCache,而共享 helper 使用 TriggerProviderCredentialsCache,不过提交后的 delete_cache_for_subscription 调用已覆盖该缓存失效,Dosu 判断该替换应属安全。此外,对刷新路径的回归测试应使用一个不在 OAuth client schema 中、但声明为 secret 的 access_token 字段。
问题场景
在 Dify 自托管源码部署中,安装一个支持 OAuth2 订阅、且订阅凭据 schema 中声明了至少一个 secret-input 字段(例如 access_token、refresh_token)的 Trigger 插件。创建 OAuth2 订阅后,调用 TriggerProviderService.refresh_oauth_token 刷新 OAuth token,随后再通过 list / get / dispatch / refresh_subscription 读取该订阅,即出现凭据加密与解密不一致的问题。
报错原文
Trigger OAuth token refresh encrypts/decrypts subscription credentials with the OAuth client schema
原因分析
在 api/services/trigger/trigger_provider_service.py 中,refresh_oauth_token 使用 provider_controller.get_oauth_client_schema()(包含 client_id / client_secret)来构造 ProviderConfigEncrypter;而 add / update / list / get / refresh_subscription 使用的是 credentials schema,即 get_credential_schema_config(credential_type)。
ProviderConfigEncrypter 只处理其所接收 schema 中声明为 secret-input 的字段,因此两条路径对“哪些字段是密文”的判断不一致:刷新前,token 字段不会被解密(client schema 未将其声明为 secret),导致密文被传给 refresh_credentials;刷新后,新的 token 以明文持久化,后续使用 credentials schema 读取时会尝试解密明文,从而得到错误数据或解密错误。
Dosu 在回复中确认了该源码级不匹配,并指出 refresh_subscription 走的是 create_trigger_provider_encrypter_for_subscription,该 helper 基于 controller.get_credential_schema_config(subscription.credential_type) 构造 encrypter。至于同一文件中其他 get_oauth_client_schema() 调用(约第 620、720、770 行),Dosu 表示看起来用于 OAuth client 配置处理,但未详细阅读,因此不宜直接断定为问题点。
环境排查
- 确认 Dify 部署方式与版本:Issue 记录为 Self Hosted(Source),main 分支源码分析位置 141cbf9832,并称 1.16.1 也存在该问题。
- 确认所安装的 Trigger 插件是否支持 OAuth2 订阅。
- 检查插件订阅凭据 schema 中是否声明了
secret-input字段(如access_token、refresh_token),且这些字段不在 OAuth client schema 中。 - 核对
refresh_oauth_token当前构造 encrypter 的方式是否仍基于get_oauth_client_schema()。 - Issue 未提供操作系统、Python、CUDA、显卡等环境信息,这些项目无需作为本问题的必要排查条件。
解决步骤
- 按 Issue 的复现描述,先用一个凭证 schema 中包含 secret
access_token的 OAuth2 Trigger 插件订阅确认现象:刷新 token 后重新读取订阅,观察是否返回错误数据或解密错误。 - 对
refresh_oauth_token的加解密路径进行修正,使其不再使用get_oauth_client_schema()构造 encrypter,这是 Issue 中明确指出的根因所在。 - 可优先尝试 #43593 提出的方向:在
refresh_oauth_token中复用create_trigger_provider_encrypter_for_subscription,使刷新路径与 add / update / list / get /refresh_subscription使用同一个 credentials schema encrypter。 - 替换时确认缓存语义:共享 helper 使用
TriggerProviderCredentialsCache,而原路径使用NoOpProviderCredentialCache;Dosu 指出提交后的delete_cache_for_subscription已使该缓存失效,因此替换应无额外风险,但仍建议实测。 - 为刷新路径补充回归测试:使用 credentials schema 中声明为 secret、但不在 OAuth client schema 中的
access_token字段,覆盖“刷新前解密 → 刷新 → 加密持久化 → 再次读取”的完整链路。
验证方法
修复后重新执行原复现步骤:在包含 secret token 字段的 OAuth2 订阅上触发 refresh_oauth_token,再通过 list / get / dispatch / refresh_subscription 读取订阅。若不再出现密文直接被传给 refresh_credentials、刷新后的 token 以密文存储且能正常解密读取,即说明两条路径已一致。以回归测试通过作为最终确认依据更可靠。
参考来源
AI 工具推荐
想把多个 AI 模型放在一个入口?
GamsGo AI 集成 ChatGPT、DeepSeek、Gemini、Claude、Midjourney、Veo 等常用模型,适合写作、绘图、视频和日常 AI 工作流。
推广链接:通过此链接购买,我可能获得佣金,不影响你的价格。
这个方案解决了吗?
可以继续搜索完整报错,或查看同一工具的其他排查指南。


