人工智能AI 应用AI 评测RAG微调提示工程开发工具桌面应用【免费下载链接】KilnBuild, Evaluate, and Optimize AI Systems. Includes evals, RAG, agents, fine-tuning, synthetic data generation, dataset management, MCP, and more.项目地址https://gitcode.com/gh_mirrors/ki/Kiln点击查看免费下载本文基于 Kiln 仓库中 GitHub OAuth Token Refresh 功能规格status: complete结合app/desktop/git_sync/与libs/core/kiln_ai/utils/config.py的现有源码系统讲解 Kiln 如何捕获、持久化并自动续期 GitHub App 的用户访问令牌user access token使 Git 同步在约 6 个月的 refresh token 生命周期内无需用户干预持续工作。读完本文你将掌握 OAuth 令牌捕获、主动/被动双通道续期、令牌轮换持久化、错误分类401 vs 503以及并发串行化等完整设计方案并能对照当前仓库源码识别问题现状与目标设计之间的差异。一、背景与问题根因OAuth 访问令牌为何会在 8 小时后集体失效Kiln 的 Git 同步Git Sync完全运行在桌面端客户端中app/desktop/git_sync/底层使用 pygit2 执行 fetch/push。对于通过Kiln AI GitHub SyncGitHub App 连接的项目auth_modegithub_oauthOAuth 用户访问令牌被用作 git HTTPS 密码# app/desktop/git_sync/clone.py 中 make_credentials() 的 github_oauth 分支 if allowed_types pygit2.enums.CredentialType.USERPASS_PLAINTEXT: return pygit2.UserPass(usernamex-token, passwordtoken)问题在于当 GitHub App 开启Expire user authorization tokens默认行为后用户访问令牌是短时效的——GitHub 默认在 8 小时左右使其失效但同时会返回一个有效期约6 个月的refresh_token与expires_in供客户端静默续期。而 Kiln 当时把这一切都丢弃了具体根因有三点均可在当前仓库源码中逐一印证token 交换时只取 access_token当前 oauth.py 的exchange_code_for_token()只返回data[access_token]refresh_token、expires_in、refresh_token_expires_in全部被丢弃。同时 OAuthFlowState 也只保存oauth_token没有任何过期时间或 refresh token 字段。配置模型没有存储位GitSyncProjectConfigTypedDict仅有pat_token与oauth_token无法保存 refresh token 或过期时间戳。全代码库没有续期逻辑整个仓库中不存在grant_typerefresh_token的调用也没有任何过期检查本文所述设计的所有相关标识符目前仅存在于 specs/projects/git_oauth_token_refresh/ 的规格文档中。由此产生的直接后果是当访问令牌过期后pygit2 的凭据回调被远端拒绝抛出pygit2.GitError(Authentication failed...)而 GitSyncManager.fetch() 会把认证错误包装成RemoteUnreachableError最终经 middleware.py 的 ERROR_MAP 映射为HTTP 503与误导性的Cannot sync with remote. Check your connection.。用户只能手动重新连接才能恢复——这正是规格文档要解决的认证过期被误报为网络问题的核心矛盾。二、目标与非目标2.1 三大目标捕获并持久化用户通过 OAuth 连接/重连时将 GitHub 返回的refresh_token与令牌过期时间戳一并保存。自动续期在 refresh token 生命周期约 6 个月内让 OAuth 访问令牌自动保持有效零用户操作。准确分类错误当令牌确实无法续期时给出清晰、分类正确的重新连接 GitHub错误而非含糊的检查网络。2.2 明确不做的事不改动 PAT 与 SSH 认证流程pat_token、system_keys用户不受影响。不提供恢复 UI也不修复任务选择器的[Object object]渲染问题——那属于独立的 git_auth_recovery 项目该项目消费本设计产出的 401AuthExpiredError。不做 server-to-serverinstallation token认证本设计保持用户访问令牌OAuth流程不变仅新增续期环节。三、核心行为设计B1–B7 七个行为B1. 令牌捕获连接与重连当exchange_code_for_token()成功时从 GitHub 响应中捕获以下字段并在交换时刻于服务端计算为绝对时间戳access_tokenrefresh_token可能缺失见 B6expires_in→ 换算为绝对时间oauth_token_expires_atepoch 秒墙钟时间refresh_token_expires_in→ 换算为绝对时间oauth_refresh_token_expires_at这些字段沿两条既有路径落库新建连接走 OAuth 状态 → 向导 →save_config路径重连走PATCH /config路径对应 git_sync_api.py 的api_update_config。最终统一持久化在GitSyncProjectConfig中。B2. 主动续期每次远端操作前对于github_oauth项目任何 fetch/push 之前执行检查若now oauth_token_expires_at - REFRESH_SKEWskew 取 300 秒即到期前 5 分钟则用 refresh token 发起续期、持久化新令牌并以新 access token 执行本次操作。若令牌仍有效或过期时间未知见 B6则什么都不做。该逻辑在设计中收敛为GitSyncManager._ensure_valid_oauth_token()方法在fetch()与commit_and_push()的 push 之前调用。B3. 被动续期认证被拒时兜底当 fetch/push 因认证拒绝而非网络错误失败且该项目有 refresh token、本次操作尚未尝试过续期时无视 skew 强制续期一次并重试该操作。这覆盖了主动检查遗漏的两种情况——本地时钟偏移clock skew与服务端令牌提前失效如令牌被吊销或 GitHub 内部轮换。B4. 刷新令牌轮换GitHub 在每次续期时都会轮换 refresh token响应中携带一个全新的refresh_token旧 token 随即失效。因此设计强制要求每次续期成功立即持久化新 refresh token 与新过期时间只在成功响应后落库失败时保持已存储凭据原样不动绝不写半成品状态。B5. 正确的错误分类这是本文档的核心价值之一把认证过期与网络故障彻底分开场景异常HTTP提示语认证无法续期refresh token 缺失/过期、续期请求返回 OAuth 错误、pygit2 报告凭据被拒且被动续期也失败AuthExpiredError新增401GitHub authorization expired. Reconnect your GitHub account in the projects Git Sync settings.真正的网络/远端故障超时、DNS、连接拒绝、非认证类pygit2.GitErrorRemoteUnreachableError保留503Cannot sync with remote. Check your connection.不变B6. 无过期时间的令牌App 设置关闭 / 旧版若 GitHub 未返回expires_in与refresh_token即 App 的 Expire user authorization tokens 选项关闭说明 access token 是长效的此时存储oauth_token_expires_at None、oauth_refresh_token None主动续期为 no-op令牌原样使用同步无限期正常工作。规格文档特别给出一个运维提示要让续期路径真正生效GitHub App 必须开启 Expire user authorization tokensGitHub App 设置 → Optional features。生产 App 几乎必然已开启——因为正是该选项才制造了 8 小时失效这个 bug——但应该验证而非假设这一点被明确列入验证清单。B7. 存量 OAuth 用户迁移现有配置只有oauth_token没有 refresh token 与过期时间expires_at None。设计行为是继续使用存量令牌直到它失败失败时直接报AuthExpiredError401 重新连接提示因为此时无 refresh token 可用。用户重连后新令牌包含 refresh token此后自动续期便正常工作。无需编写任何迁移脚本。四、边界情况与错误处理策略无 refresh token 但令牌已过期旧版用户或关闭过期选项的用户直接AuthExpiredError不进入死循环重试。续期请求本身网络失败优先以AuthExpiredError呈现提示用户重连而非无限静默重试——因为 refresh token 本身未经验证无法干净地区分GitHub 宕机与refresh token 已死选择更具可操作性的重连提示是可接受的权衡文档明确记录了这一取舍。并发续期后台同步每 10s 一轮与前台请求可能同时撞上过期点。续期必须按仓库串行化 双重检查过期状态确保单次使用的 refresh token 只被消费一次。后台同步的认证失败记录清晰日志并保持下次轮询重试的既有行为后台同步绝不能因AuthExpiredError崩溃。多 Kiln 项目共享同一克隆罕见的同一仓库二次导入场景续期只会写入当前 manager 所属项目的配置共享该克隆的其他项目可能在下次续期/重启前保留过期令牌。这是文档记录在案的已知限制常规一克隆一项目场景完全正确。五、数据契约与 API 变更5.1GitSyncProjectConfig新增三个字段oauth_refresh_token: str | None oauth_token_expires_at: float | None # epoch seconds, wall-clock oauth_refresh_token_expires_at: float | None配套两个要求libs/core/kiln_ai/utils/config.py 中git_sync_projects属性的sensitive_keys需从[pat_token, oauth_token]扩展为[pat_token, oauth_token, oauth_refresh_token]确保日志与导出中的脱敏redaction覆盖新敏感字段——当前仓库的脱敏机制见同一文件第 362-364 行的sensitive_keys遍历逻辑。get_git_sync_config()必须通过.get(...)将三个新字段默认化为None旧配置缺少这些键save_git_sync_config()已是整字典写入仅需扩展 TypedDict。此外设计引入一个同步读-改-写的聚焦辅助函数update_oauth_tokens(project_path, result)只更新单个项目的 OAuth 令牌字段且读取与写入之间不允许await防止协程交错产生部分更新。5.2OAuthTokenResult数据类oauth.py 新增dataclass class OAuthTokenResult: access_token: str refresh_token: str | None None access_token_expires_at: float | None None # epoch seconds (wall-clock) refresh_token_expires_at: float | None None在交换/续期时刻计算GitHub 返回expires_in时expires_at time.time() expires_in否则为None。5.3 API 字段全部为 additive、可选、向后兼容OAuthStatusResponse新增oauth_refresh_token、oauth_token_expires_at、oauth_refresh_token_expires_at。SaveConfigRequest与UpdateConfigRequest接受同样的三个字段并持久化。GitSyncConfigResponse不暴露任何原始令牌维持现状可选新增布尔has_oauth_refresh_token用于诊断。5.4 错误 → HTTP 映射middlewareERROR_MAP异常状态码DetailAuthExpiredError新增401GitHub authorization expired. Reconnect your GitHub account in the projects Git Sync settings.RemoteUnreachableError503Cannot sync with remote. Check your connection.不变值得注意的仓库现状当前 errors.py 已存在GitAuthErrorGit authentication failed or expired (bad/expired token, 401/403)且 ERROR_MAP 已将其映射为 401 Re-import the project to reconnect with fresh credentials.。可以说仓库已具备认证错误 → 401的分类雏形而本规格在此基础上新增面向 OAuth 续期场景的AuthExpiredError与专属 reconnect 文案。六、配置与默认值参数值说明REFRESH_SKEW_SECONDS300到期前 5 分钟内即触发主动续期提前量容忍时钟偏差与调度延迟GitHub token 端点https://github.com/login/oauth/access_token续期与 code 交换共用同一端点grant_typerefresh_tokenhttpx 续期超时30s与现有 code 交换调用一致当前 exchange_code_for_token 即使用timeout30.0七、并发与正确性约束单进程 / 单事件循环桌面端是单个 FastAPI 进程因此按仓库粒度使用asyncio.Lock即可满足续期串行化——无需跨进程分布式锁。锁内要重新读取配置做双重检查double-checked expiry等待锁的协程获锁后不应再重复续期保证 GitHub 的单次使用 refresh token 恰好被消费一次。配置写入必须同步update_oauth_tokens的读-改-写全程无await防止协程在事件循环上交错执行部分更新。时钟语义过期判断使用墙钟time.time()令牌有效性本质是墙钟时间time.monotonic()仅用于内存中 OAuth 流程的 TTL当前 OAuthFlowState.created_at 与OAUTH_TIMEOUT_SECONDS 300正是如此。八、安全边界refresh token 会经localhost 前端sessionStorage传输并以明文形式存储在配置 YAML 中——与现有oauth_token的存储方式完全一致。对本地桌面应用而言可接受且由sensitive_keys脱敏兜底。续期成功后才持久化失败不写任何状态杜绝脏数据。九、仓库现状对照哪些已具备哪些待实现以当前仓库为准规格文档status: complete但对应代码改动尚未落地逐项对照规格目标当前仓库现状证据位置捕获 refresh_token / 过期时间未实现exchange_code_for_token仅返回 access_token其余字段被丢弃oauth.py配置存储 refresh_token / 过期时间未实现GitSyncProjectConfig仅有oauth_tokenconfig.pysensitive_keys覆盖 refresh_token未实现目前仅[pat_token, oauth_token]libs/core/kiln_ai/utils/config.py续期引擎_ensure_valid_oauth_token未实现manager 无 refresh 逻辑、无_refresh_lockgit_sync_manager.py认证 vs 网络错误识别已具备雏形_AUTH_ERROR_MARKERS与_is_auth_error()已存在匹配 authentication/authoriz/credential/401/403 等git_sync_manager.py认证错误 → 401已具备雏形GitAuthError → 401已映射但文案面向重新导入项目而非OAuth 重连middleware.py前端携带新字段未实现OAuthFlowCallbacks.onSuccess(token: string)、stored_token均只持有单值令牌oauth_flow.ts、oauth_with_install.ts从源码结构可以推断_is_auth_error与GitAuthError → 401映射是本次改造的既有土壤规格中的被动续期判定可直接复用该函数而AuthExpiredError可视为GitAuthError在 OAuth 续期语义下的细化。前端一侧规格要求将onSuccess回调签名从(token: string)演进为载荷对象{ oauth_token, oauth_refresh_token, oauth_token_expires_at, oauth_refresh_token_expires_at }且前端时间戳为不透明透传值——由服务端计算、前端只负责从 OAuth 状态搬运到 save/update config不做任何解析或续期。十、测试与验证策略10.1 后端pytest位于 app/desktop/git_sync/test_*.py integration_testsoauthexchange_code_for_token解析全部字段并计算expires_atmock httpx缺失expires_in→Nonerefresh_access_token成功 轮换 失败抛OAuthError通过 patchtime.time使过期判断可确定性测试。config新字段往返旧配置缺键默认Noneupdate_oauth_tokens只更新令牌字段、对未知 project_path 为 no-op。manager_ensure_valid_oauth_token覆盖——有效不发网络请求、skew 内不发请求、过期有 refresh发起续期并持久化、更新_oauth_token、过期无 refresh抛AuthExpiredError、非 OAuth 模式no-op、forceTrue并发下两个等待者只产生一次网络续期_is_auth_error分类fetch()/commit_and_push()被动续期 单次重试ensure_fresh*透传AuthExpiredError。middlewareAuthExpiredError → 401 文案RemoteUnreachableError仍 503。api回调把 refresh 字段存入 flowstatus 返回它们save/update 持久化auth_mode 切换清空 refresh 字段。background_syncAuthExpiredError被记录且轮询循环存活。10.2 前端vitestoauth_flow.test.tsonSuccess收到载荷对象status mock 含新字段。api.test.ts/ 向导 store 测试新字段进入 save/update 请求体。10.3 端到端验证uv run ./checks.sh --agent-modelint、format、ty、pytest、web lint/format/check/test/build、schema check。Schema 再生成运行app/web_ui/src/lib/generate_schema.sh确认app/web_ui/src/lib/check_schema.sh通过。手工验证可选OAuth 连接一个仓库后把配置中oauth_token_expires_at改为近过去的值强制触发 B2确认 fetch 触发续期新令牌已持久化且同步继续再清空oauth_refresh_token确认下一次操作返回401 reconnect 文案而非 503。前置条件核对确认 GitHub App 已开启 Expire user authorization tokensGitHub App 设置 → Optional features——这是 GitHub 愿意签发 refresh token 的前提对应 B6。十一、小结GitHub OAuth Token Refresh 设计给出了一个完整的预防为主、兜底为辅的令牌生命周期方案B1/B4 保证 refresh token 与过期时间被捕获并随轮换及时落库B2 主动续期让令牌在到期前 5 分钟自动更新B3 被动续期在认证被拒时强制续期并重试一次B5 则从根上修正了认证过期被误报为网络故障的体验问题——不可续期时返回 401 重新连接 GitHub 账户而真正的网络故障保持 503。叠加按仓库串行化的asyncio.Lock双重检查、同步读-改-写持久化与sensitive_keys脱敏这套方案在本地单进程桌面应用的约束下做到了正确、安全、可测试。对使用github_oauth模式的 Kiln 用户而言其效果是8 小时的失效魔咒被打破同步在约 6 个月的 refresh token 周期内无需任何用户操作。赞分享人工智能AI 应用AI 评测RAG微调提示工程开发工具桌面应用【免费下载链接】KilnBuild, Evaluate, and Optimize AI Systems. Includes evals, RAG, agents, fine-tuning, synthetic data generation, dataset management, MCP, and more.项目地址https://gitcode.com/gh_mirrors/ki/Kiln点击查看免费下载相关推荐Kiln 桌面端 GitHub OAuth 令牌自动续期架构解析8 小时过期、静默刷新与 401 精确定位Kiln 桌面端 GitHub OAuth 令牌自动续期架构解析8 小时过期、静默刷新与 401 精确定位 导读 本文围绕 Kiln 仓库中 git_oaut人工智能AI 应用AI 评测RAG微调提示工程开发工具桌面应用MLOpsTolaria 的 GitHub Device Flow OAuth桌面端 git 仓库同步的认证架构决策与实践Tolaria 的 GitHub Device Flow OAuth桌面端 git 仓库同步的认证架构决策与实践 本文基于 Tolaria演进前代号 Lap桌面应用知识管理AI 应用MCP 服务Wox 启动器内置倒计时从查询框、桌面浮层到重启续时全解析Wox 启动器内置倒计时从查询框、桌面浮层到重启续时全解析 Wox 在启动器窗口内直接内置了倒计时功能不需要安装任何第三方插件只需输入 timer 加时长桌面应用AI 应用插件系统上一篇Windows 11终极指南如何用WSA Toolbox轻松安装Android应用下一篇Windows 11安卓应用革命WSA Toolbox终极指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考