![[Feature]: MCP OAuth token endpoint should not fabricate expires_in when upstream omits it](https://www.chat-gpts.plus/wp-content/uploads/2026/06/30298-96794208.jpg)
[Feature]: MCP OAuth token endpoint should not fabricate expires_in when upstream omits it
快速结论:该问题在 LiteLLM 代理中处理 MCP OAuth 令牌交换时发生。当上游身份提供商(IdP)的令牌响应中未包含 expires_in 字段时,LiteLLM 默认将其设为 3600 秒(1 小时),导致非过期令牌(如 GitHub OAuth App 令牌)被客户端标记为已过期,触发不必要的浏览器重新认证。优先排查 LiteLLM 版本是否已包含修复。
问题场景
用户在使用 LiteLLM 代理(Proxy)功能,通过 MCP(Model Context Protocol)与 GitHub、Linear 等 OAuth App 提供商交换令牌时触发。这些提供商颁发非过期令牌且有意省略 expires_in 字段。问题影响依赖该字段的 MCP 客户端(如 Claude Code、opencode),导致有效令牌在 1 小时后被标记为过期,且因无 refresh_token 而强制用户完成完整的浏览器重新认证流程。
报错原文
# LiteLLM MCP OAuth token endpoint 中 fabricate 默认值的关键代码(位于 discoverable_endpoints.py:515):
"expires_in": token_response.get("expires_in", 3600),
# 修复后的代码:
result = {
"access_token": access_token,
"token_type": token_response.get("token_type", "Bearer"),
}
if token_response.get("expires_in") is not None:
result["expires_in"] = token_response["expires_in"]
原因分析
LiteLLM 在 exchange_token_with_server() 方法中(discoverable_endpoints.py 的约第 515 行)处理上游令牌响应时,使用 token_response.get("expires_in", 3600) 为缺失的 expires_in 字段提供了硬编码默认值 3600 秒。根据 RFC 6749 第 5.1 节,expires_in 是 RECOMMENDED 而非 REQUIRED 字段,仅当授权服务器知道实际令牌生命周期时才应包含。省略此字段表示“生命周期未知或无限”。LiteLLM 默认填充 3600 的行为改变了令牌响应的语义,导致非过期令牌被错误地视为有时效性。此外,许多提供商(如 GitHub、Linear)也不返回 refresh_token,客户端在令牌被标记为过期后无法静默刷新,只能强制用户重新进行 OAuth 认证。
环境排查
- LiteLLM 版本:确认是否使用低于修复提交(commit
07dffa855e,PR #29951)的版本。该修复在litellm_internal_staging分支中合并于 2026 年 6 月 8 日。 - 上游身份提供商(IdP):确认 OAuth 提供商(如 GitHub、Linear 或其他企业 IdP)的令牌响应是否省略了
expires_in和refresh_token字段。 - MCP 客户端:确认客户端(如 Claude Code、opencode)是否依赖
expires_in字段来判断令牌有效性。
解决步骤
- 升级 LiteLLM(可优先尝试):等待并安装下一个从 staging 分支发布的 LiteLLM 版本,该版本已包含修复(commit
07dffa855e,PR #29951)。修复内容为:仅在token_response中明确包含expires_in且其值不为None时,才将其放入最终令牌响应中。 - 手动临时修复(如无法升级):找到
discoverable_endpoints.py文件中exchange_token_with_server()函数(约第 515 行),将:"expires_in": token_response.get("expires_in", 3600),修改为:
if token_response.get("expires_in") is not None: result["expires_in"] = token_response["expires_in"]并确保
result字典构建逻辑与此匹配。 - 配置可选覆盖(推测性):对于上游返回错误或缺失值的情况,考虑在
MCPServer配置中添加token_response_expires_in字段(类型为Optional[int]),允许管理员手动覆盖。此功能在 Issue 中被提及但尚未在修复中实现。
验证方法
修复后,使用 GitHub 或 Linear 等省略 expires_in 的 OAuth 提供商进行 MCP 令牌交换。确认客户端(如 Claude Code)在 1 小时后不会触发重新认证,且令牌被视为非过期或永久有效。LiteLLM 新增回归测试用例 test_token_exchange_omits_expires_in_when_upstream_omits_it 和 test_token_exchange_passes_through_upstream_expires_in,可运行这些测试确认修复有效。

![[分享发现] [自制小工具分享] AI 老照片修复站上线,送点小礼物给大家 🎁](https://www.chat-gpts.plus/wp-content/uploads/2026/06/ai_cover_5-551-768x403.jpg)
![[程序员] codex 赠送的重置一次,今天用了,感谢上个帖子中的朋友](https://www.chat-gpts.plus/wp-content/uploads/2026/06/ai_cover_4-557-768x403.jpg)
