拓十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Operit GitHub OAuth Broker 云端交换协议:PKCE 短期认证事务与一次性领取凭据实战解析

Operit GitHub OAuth Broker 云端交换协议:PKCE 短期认证事务与一次性领取凭据实战解析 AI Agent人工智能大模型AI 应用工具调用本地部署MCP ClientsAgent 记忆【免费下载链接】OperitThe most powerful AI agent and AI chat software on Android/Operit是一款Android上能力最为强大、发展最久的AI Agent项目地址https://gitcode.com/gh_mirrors/op/Operit点击查看免费下载导读本文围绕 Operit 项目中docs/TODO/github_oauth_broker/1_CloudBroker.md所定义的云端交换协议展开讲解 Operit 如何把 GitHub OAuth 的授权码交换从 Android 设备端迁移到api.operit.app的受保护 Worker并用 PKCE verifier、一次性领取凭据claim credential与短期认证事务构建设备不接触 client secret、敏感凭据不落地浏览器 URL的登录链路。读完本文你将掌握该协议的事务创建、完成回调、单次领取三阶段设计以及它在 Android 与 Operit 2Rust CLI / Flutter两代客户端中的落地方式并可直接对照仓库源码复现每一条安全边界。背景client secret 为何不能留在 APK 里在进入新协议之前先看旧实现为什么必须被替换。docs/TODO/github_oauth_broker/index.md记录了这次改造的直接动因Android 客户端曾将 GitHub OAuth client secret 写入 BuildConfig并在设备上直接交换授权码。该 secret 随已发布 APK 分发不能继续作为可信凭据。这是移动端 OAuth 的经典悖论APK 可以轻易被反编译编入 BuildConfig 的 client secret 等同于公开同时授权码在设备端换取 token意味着任何能读到 APK 的人都能冒充客户端完成同样的交换。旧协议里设备向 GitHub 直接提交 OAuth 授权码和编入 APK 的 client secret见 1_CloudBroker.md因此被判定为不可继续使用。改造目标被明确写进 index.md新版 Android 只通过api.operit.app的受保护 Worker 完成 GitHub 授权码交换新 APK 不再包含 client ID、client secret 或operit://OAuth 回调已发布的旧 APK 继续使用原 OAuth App直至发布公告规定的迁移截止日新 OAuth App 的 client secret 只存在于 Cloudflare Worker secret。其中最后一条是整套协议的安全基石secret 只存在于云端设备端永远拿不到。旧实现凭据落地的三种路径1_CloudBroker.md与配套的 2_AndroidClient.md、3_Operit2Client.md 共同勾勒了改造前的全貌主要有三条路径客户端旧实现方式安全问题AndroidGitHub 登录界面提供内嵌 WebView 与外部浏览器两条路径应用接收operit://github-oauth-callback后直接向 GitHub 交换 token授权码经自定义 scheme 回传client secret 在设备上参与交换Rust CLIOperit 2使用 GitHub Device Flow并要求操作者设置 GitHub OAuth client ID 环境变量凭据进入环境变量流程与浏览器式授权割裂Flutter 市场页Operit 2要求用户创建并粘贴 GitHub Token用户长期令牌被手动粘贴进客户端风险面大旧 Android 实现的另一个隐患是自定义 scheme 与 Activity Intent 接管operit://github-oauth-callback这样的 scheme 属于全局可声明标识存在被其他应用抢占的风险同时浏览器回调 URL 会真实携带授权码。新协议的目标之一就是把授权码、token 和领取凭据全部排除在浏览器回调 URL 之外见 1_CloudBroker.md 的结果清单。新协议核心短期认证事务三阶段新实现的协议骨架定义在 1_CloudBroker.mdWorker 创建短期认证事务生成 PKCE verifier 与一次性领取凭据。GitHub 回调由 Worker 接收并交换授权码用户 token 加密暂存后重定向到浏览器回调 Host 预先注册的完成地址Core 校验完成链接后以领取凭据 claim 一次记录随即删除。把这段话拆解为三个阶段的时序事务创建start客户端向 Worker 提交自己准备的回调完成地址completion redirect URIWorker 创建短期认证事务生成 PKCE verifier 与一次性领取凭据delivery credential返回授权页面 URL、事务 ID、凭据与过期时间授权与回调complete用户浏览器访问 GitHub 授权页授权码回调由 Worker 接收并完成 PKCE 交换获取的 token 在 Worker 侧加密暂存随后把浏览器重定向到客户端预注册的完成地址完成链接中只携带事务状态单次领取claim客户端Core校验完成链接与当前事务匹配后用一次性领取凭据 claim 一次Worker 返回 token 与用户信息服务端记录随即删除。这套设计同时满足四个约束对应 1_CloudBroker.md 的结果client secret 不离开 Cloudflare secret不落入 APK、不进入请求体授权码、token 和领取凭据不出现在浏览器回调 URL 中回调 URL 只携带transactionId与status等非敏感参数完成通知不产生 Worker 轮询请求客户端一次 start、一次 claim没有状态轮询新接口不改动旧客户端使用的/market/v2/auth/github市场旧接口保持兼容。协议责任划分3_Operit2Client.md 的协议责任进一步明确了边界Worker 持有 OAuth client secret、生成 PKCE 和处理 GitHub 回调客户端不包含 client ID 或 client secretFlutter 市场页不持有 OAuth HTTP、平台 Intent、EventChannel 或 loopback receiver只管理自己的可见 WebView 导航客户端不向 Worker 反复查询授权状态Rust 解析和校验 Broker 响应并以单元测试固定协议契约。客户端协议实现Broker Service 与 Coordinator云端 Worker 的行为无法在本仓库直接查看后端位于独立的marketWorker 工程但 Android 端的协议实现完整存在于本仓库可以直接对照。1.GitHubOAuthBrokerService协议的两个 HTTP 端点GitHubOAuthBrokerService.kt 是客户端侧与 Worker 通信的唯一入口基地址硬编码为https://api.operit.app见 L144。它封装了两个请求startLogin(completionRedirectUri)L60-L82POST$BROKER_BASE_URL/oauth/github/start请求体为{completionRedirectUri: ...}解析返回GitHubOAuthBrokerStartResponse该响应携带transactionId、deliveryCredential、authorizationUrl、completionRedirectUri与expiresAtL18-L25。客户端拿到后即可展示授权页同时本地私有保存领取凭据claimLogin(transactionId, deliveryCredential)L84-L128POST$BROKER_BASE_URL/oauth/github/claim请求体为{transactionId: ..., deliveryCredential: ...}。响应status必须为complete否则拒绝L108-L112随后解出accessToken、tokenType、scope、expiresIn、refreshToken与userL113-L122。两个请求都使用 30 秒超时的 OkHttpClientL49-L53错误统一封装为IllegalStateException并附带 HTTP 状态码与响应体便于排查L130-L136。响应解析使用ignoreUnknownKeys的宽松 Json 配置L55-L58保证前后端字段演进时旧客户端不因多余字段崩溃。2.GitHubOAuthCoordinator事务生命周期管理GitHubOAuthCoordinator.kt 是客户端侧的编排中枢startLogin(completionRedirectUri)L18-L33调用 Broker Service 创建事务并把transactionId、deliveryCredential、expiresAt通过GitHubAuthPreferences.saveActiveOAuthTransaction写入本地 DataStore随后返回事务供 UI 展示授权页completeLogin(completionUri)L35-L82先校验完成链接的transactionId与当前活动事务一致L39-L41否则直接失败随后按status参数分流——complete继续领取、denied视为用户取消并清事务、error透传错误信息、其余视为非法状态确认完成后调用claimLogin领取一次 token保存认证信息并清除活动事务cancelLogin()L84-L86用户取消时清空活动事务避免遗留凭据被复用。值得注意的细节内嵌登录使用固定完成地址https://api.operit.app/oauth/github/completeL90而外部浏览器登录则使用 loopback 临时地址见下文两者都会在 start 时预注册给 Worker对应文档中重定向到浏览器回调 Host 预先注册的完成地址。3. 事务凭据的持久化GitHubAuthPreferencesGitHubAuthPreferences.kt 基于 DataStoregithub_auth_preferencesL19-L20管理全部 GitHub 认证状态。与本次改造直接相关的设计有认证版本门槛REQUIRED_AUTH_VERSION 3L43isAuthSessionCurrentL90-L94要求本地会话的auth_version 3且授予 scope 覆盖notifications,public_repo,user:email,read:userL42旧版本认证数据不会被新认证代码继续使用呼应 2_AndroidClient.md 的认证版本升级旧 APK 数据不会被新版认证代码继续使用活动事务三键active_oauth_transaction_id、active_oauth_delivery_credential、active_oauth_expires_atL55-L57getActiveOAuthTransactionL245-L258在读回时会检查过期并自动清除杜绝过期凭据被 claim领取后清理saveAuthInfoL131-L165在写入 token 的同时移除活动事务三键与服务端记录随即删除形成两端对称的单次语义。Android 登录 UI通用浏览器回调组件与双路径GitHubLoginDialog.kt 保留了内嵌 WebView与外部浏览器两条登录路径L50-L54 的GitHubLoginMode三态CHOOSER / EMBEDDED / EXTERNAL但底层机制全部替换。内嵌路径BrowserCallbackDialog通用组件内嵌登录不再持有 GitHub 协议逻辑而是复用通用组件 BrowserCallbackDialog.kt注释明确写着 Presents one host-owned browser flow and reports navigation to its registered callback destination。它只负责三件事加载authorizationUrlL109-L111在shouldOverrideUrlLoading与onPageStarted两个时机捕获导航L79-L95用matchesCallbackDestinationL189-L194按scheme / host / port / path 四元组匹配完成地址命中即回调onCompletion(uri)并stopLoading()处理超时与释放expiresAt到期未完成则回调onFailureL113-L127releaseBrowserCallbackWebViewL197-L208在释放时依次执行停止加载、about:blank、清历史、移除视图、destroy()避免 WebView 泄漏。由于完成地址是https://api.operit.app/oauth/github/complete这样的 https 地址而非自定义 scheme2_AndroidClient.md 的结果清单中的三项随之成立删除旧自定义 scheme、外部浏览器回调和 Activity Intent 接管浏览器回调组件不包含 GitHub 协议、token 或领取凭据删除 Android 的 client ID 与 client secret BuildConfig 字段。外部路径一次性 loopback 接收器外部浏览器登录使用 GitHubOAuthLoopbackCallbackServer.kt 在127.0.0.1上临时监听一个端口要求端口号 1024L98、L112-L114完成地址为http://127.0.0.1:port/oauth/github/completeL15-L22。awaitCompletionL24-L40只接受一次 GET 请求校验路径与完成地址一致L63-L66把查询参数拼接回完成 URI 后返回 200其余请求返回 404。整个流程在 GitHubLoginDialog.kt 的GitHubExternalLoginDialog中L240-L301用withTimeout(remainingMillis)包裹超时即按登录失败处理finally中关闭服务器并清理事务——这与文档完成通知不产生 Worker 轮询请求的约束一致因为整个链路只有一次授权页展示和一次完成回调。登出与会话隔离2_AndroidClient.md 还规定了一个易被忽略的体验细节用户明确退出 GitHub 登录时应用会清除自身 WebView 的 Cookie 和 WebStorage再删除本地认证信息。下一次登录不会静默复用之前的 GitHub Web 会话这不会影响系统浏览器或 Chrome 的 GitHub 登录状态。即退出登录需要同时清 WebView 会话与本地认证数据但作用域严格限定在应用自身 WebView避免误伤系统浏览器中用户已登录的 GitHub 账号。Operit 2 客户端迁移类型化服务替代命令字符串Operit 2Rust CLI 与 Flutter 市场的迁移方向与 Android 一致但多了一个架构约束3_Operit2Client.md 要求两端都调用 Core 的类型化GitHubOAuthBrokerServiceFlutter 使用生成的 Dart proxy、CLI 使用生成的 Rust proxy两者都不传递市场认证命令字符串、不解析命令 stdout也不手写 CoreLink 请求。具体分工应用自己准备完成地址Core 用该地址调用 Worker 的/oauth/github/start并私有保存 delivery credential应用展示授权页并交回完成链接——CLI 使用临时 loopback 并在终端打印授权链接Flutter 市场登录对话框拦截其 WebView 的完成导航Core 只在收到与当前事务、目标地址都匹配的完成链接后 claim 一次并保存 Worker 返回的 GitHub tokenRust 侧通过单元测试固定协议契约Rust 解析和校验 Broker 响应并以单元测试固定协议契约。这条迁移路径的意图很清晰旧实现里 CLI 依赖环境变量 client ID、Flutter 要求用户粘贴 token、两端以命令字符串与 stdout 解析方式对接市场认证都属于凭据或协议细节外泄的脆弱设计新实现把协议收敛为类型化的 start/claim 调用凭据只在 Core 与 Worker 之间传递。结果清单与安全收益汇总综合 1_CloudBroker.md 与 2_AndroidClient.md 的结果章节本次改造的验收标准如下client secret 不离开 Cloudflare secret任何客户端二进制与请求体中都不可见授权码、token 和领取凭据不出现在浏览器回调 URL 中回调 URL 仅携带事务 ID 与状态完成通知不产生 Worker 轮询请求全链路仅 start / claim 两次 HTTP 往返新接口不改动旧客户端使用的/market/v2/auth/github旧接口保持兼容直至迁移截止日Android 删除旧自定义 scheme、外部浏览器回调和 Activity Intent 接管浏览器回调组件不含 GitHub 协议、token 或领取凭据Android 删除 client ID 与 client secret 的 BuildConfig 字段认证版本升级到 3旧 APK 数据不会被新认证代码继续使用。从实现事实看这些收益都能在本仓库的源码中得到印证GitHubOAuthBrokerService的请求体只有completionRedirectUri/transactionId/deliveryCredentialGitHubOAuthBrokerService.kt没有任何 client secret 字段完成链接校验依赖transactionId与状态参数GitHubOAuthCoordinator.kt仓库中已搜不到operit://github-oauth-callback或 client secret BuildConfig 字段的残留。部署顺序与现状index.md 的状态章节记录了该改造的推进节奏云端密钥已配置、新 OAuth App 已创建、D1 迁移已应用Worker 部署待后端现有未提交市场改动整理后执行Operit 2 客户端迁移进行中CLI 包当时的编译问题与本协议无关。部署顺序明确为后端先于 Android——这是合理的依赖顺序新 APK 依赖 Worker 的/oauth/github/start与/oauth/github/claim端点云端必须先就绪旧客户端才能继续使用原 OAuth App 平稳过渡到迁移截止日。对读者而言若要在自己的项目里复刻这套方案最小可复制的骨架是一台持有 client secret 的云端 Worker负责 PKCE 与授权码交换、一个短期事务存储带过期与单次领取语义、客户端侧一次 start 一次 claim 的类型化调用以及一个只按 scheme/host/port/path 匹配完成地址的通用浏览器回调组件。Operit 的 GitHubOAuthBrokerService.kt 与 GitHubOAuthCoordinator.kt 提供了现成的参考实现。赞分享AI Agent人工智能大模型AI 应用工具调用本地部署MCP ClientsAgent 记忆【免费下载链接】OperitThe most powerful AI agent and AI chat software on Android/Operit是一款Android上能力最为强大、发展最久的AI Agent项目地址https://gitcode.com/gh_mirrors/op/Operit点击查看免费下载相关推荐Operit GitHub OAuth 完成回调协议Worker 事务、应用自持浏览器与一次性 Claim 的完整交付链路Operit GitHub OAuth 完成回调协议Worker 事务、应用自持浏览器与一次性 Claim 的完整交付链路 本指南围绕 Operit 仓库中AI Agent人工智能大模型AI 应用工具调用本地部署MCP ClientsAgent 记忆GUI 自动化MCP Toolbox 之 oceanbase-execute-sql在 OceanBase 上执行 SQL 的 MCP 工具配置与实战指南MCP Toolbox 之 oceanbase execute sql在 OceanBase 上执行 SQL 的 MCP 工具配置与实战指南 oceanbasAI Agent人工智能大模型AI 应用工具调用本地部署MCP ClientsAgent 记忆GUI 自动化Open edX 认证凭证交换实战auth_exchange 模块的第三方 OAuth 接入与 Token 登录实现Open edX 认证凭证交换实战auth_exchange 模块的第三方 OAuth 接入与 Token 登录实现 导读 本文以 Open edX 平台后端教育上一篇深入解析mshumer/gpt-author项目AI自动生成小说全流程指南下一篇从0到1使用Google Workspace MCP Server构建自动化邮件处理系统创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表