議:PKCE 短期認(rèn)證事務(wù)與一次性領(lǐng)取憑據(jù)實(shí)戰(zhàn)解析)
AI Agent人工智能大模型AI 應(yīng)用工具調(diào)用本地部署MCP ClientsAgent 記憶【免費(fèi)下載鏈接】OperitThe most powerful AI agent and AI chat software on Android/Operit是一款A(yù)ndroid上能力最為強(qiáng)大、發(fā)展最久的AI Agent項(xiàng)目地址https://gitcode.com/gh_mirrors/op/Operit點(diǎn)擊查看免費(fèi)下載導(dǎo)讀本文圍繞 Operit 項(xiàng)目中docs/TODO/github_oauth_broker/1_CloudBroker.md所定義的云端交換協(xié)議展開(kāi)講解 Operit 如何把 GitHub OAuth 的授權(quán)碼交換從 Android 設(shè)備端遷移到api.operit.app的受保護(hù) Worker并用 PKCE verifier、一次性領(lǐng)取憑據(jù)claim credential與短期認(rèn)證事務(wù)構(gòu)建設(shè)備不接觸 client secret、敏感憑據(jù)不落地瀏覽器 URL的登錄鏈路。讀完本文你將掌握該協(xié)議的事務(wù)創(chuàng)建、完成回調(diào)、單次領(lǐng)取三階段設(shè)計(jì)以及它在 Android 與 Operit 2Rust CLI / Flutter兩代客戶(hù)端中的落地方式并可直接對(duì)照倉(cāng)庫(kù)源碼復(fù)現(xiàn)每一條安全邊界。背景client secret 為何不能留在 APK 里在進(jìn)入新協(xié)議之前先看舊實(shí)現(xiàn)為什么必須被替換。docs/TODO/github_oauth_broker/index.md記錄了這次改造的直接動(dòng)因Android 客戶(hù)端曾將 GitHub OAuth client secret 寫(xiě)入 BuildConfig并在設(shè)備上直接交換授權(quán)碼。該 secret 隨已發(fā)布 APK 分發(fā)不能繼續(xù)作為可信憑據(jù)。這是移動(dòng)端 OAuth 的經(jīng)典悖論APK 可以輕易被反編譯編入 BuildConfig 的 client secret 等同于公開(kāi)同時(shí)授權(quán)碼在設(shè)備端換取 token意味著任何能讀到 APK 的人都能冒充客戶(hù)端完成同樣的交換。舊協(xié)議里設(shè)備向 GitHub 直接提交 OAuth 授權(quán)碼和編入 APK 的 client secret見(jiàn) 1_CloudBroker.md因此被判定為不可繼續(xù)使用。改造目標(biāo)被明確寫(xiě)進(jìn) index.md新版 Android 只通過(guò)api.operit.app的受保護(hù) Worker 完成 GitHub 授權(quán)碼交換新 APK 不再包含 client ID、client secret 或operit://OAuth 回調(diào)已發(fā)布的舊 APK 繼續(xù)使用原 OAuth App直至發(fā)布公告規(guī)定的遷移截止日新 OAuth App 的 client secret 只存在于 Cloudflare Worker secret。其中最后一條是整套協(xié)議的安全基石secret 只存在于云端設(shè)備端永遠(yuǎn)拿不到。舊實(shí)現(xiàn)憑據(jù)落地的三種路徑1_CloudBroker.md與配套的 2_AndroidClient.md、3_Operit2Client.md 共同勾勒了改造前的全貌主要有三條路徑客戶(hù)端舊實(shí)現(xiàn)方式安全問(wèn)題AndroidGitHub 登錄界面提供內(nèi)嵌 WebView 與外部瀏覽器兩條路徑應(yīng)用接收operit://github-oauth-callback后直接向 GitHub 交換 token授權(quán)碼經(jīng)自定義 scheme 回傳client secret 在設(shè)備上參與交換Rust CLIOperit 2使用 GitHub Device Flow并要求操作者設(shè)置 GitHub OAuth client ID 環(huán)境變量憑據(jù)進(jìn)入環(huán)境變量流程與瀏覽器式授權(quán)割裂Flutter 市場(chǎng)頁(yè)Operit 2要求用戶(hù)創(chuàng)建并粘貼 GitHub Token用戶(hù)長(zhǎng)期令牌被手動(dòng)粘貼進(jìn)客戶(hù)端風(fēng)險(xiǎn)面大舊 Android 實(shí)現(xiàn)的另一個(gè)隱患是自定義 scheme 與 Activity Intent 接管operit://github-oauth-callback這樣的 scheme 屬于全局可聲明標(biāo)識(shí)存在被其他應(yīng)用搶占的風(fēng)險(xiǎn)同時(shí)瀏覽器回調(diào) URL 會(huì)真實(shí)攜帶授權(quán)碼。新協(xié)議的目標(biāo)之一就是把授權(quán)碼、token 和領(lǐng)取憑據(jù)全部排除在瀏覽器回調(diào) URL 之外見(jiàn) 1_CloudBroker.md 的結(jié)果清單。新協(xié)議核心短期認(rèn)證事務(wù)三階段新實(shí)現(xiàn)的協(xié)議骨架定義在 1_CloudBroker.mdWorker 創(chuàng)建短期認(rèn)證事務(wù)生成 PKCE verifier 與一次性領(lǐng)取憑據(jù)。GitHub 回調(diào)由 Worker 接收并交換授權(quán)碼用戶(hù) token 加密暫存后重定向到瀏覽器回調(diào) Host 預(yù)先注冊(cè)的完成地址Core 校驗(yàn)完成鏈接后以領(lǐng)取憑據(jù) claim 一次記錄隨即刪除。把這段話(huà)拆解為三個(gè)階段的時(shí)序事務(wù)創(chuàng)建start客戶(hù)端向 Worker 提交自己準(zhǔn)備的回調(diào)完成地址completion redirect URIWorker 創(chuàng)建短期認(rèn)證事務(wù)生成 PKCE verifier 與一次性領(lǐng)取憑據(jù)delivery credential返回授權(quán)頁(yè)面 URL、事務(wù) ID、憑據(jù)與過(guò)期時(shí)間授權(quán)與回調(diào)complete用戶(hù)瀏覽器訪(fǎng)問(wèn) GitHub 授權(quán)頁(yè)授權(quán)碼回調(diào)由 Worker 接收并完成 PKCE 交換獲取的 token 在 Worker 側(cè)加密暫存隨后把瀏覽器重定向到客戶(hù)端預(yù)注冊(cè)的完成地址完成鏈接中只攜帶事務(wù)狀態(tài)單次領(lǐng)取claim客戶(hù)端Core校驗(yàn)完成鏈接與當(dāng)前事務(wù)匹配后用一次性領(lǐng)取憑據(jù) claim 一次Worker 返回 token 與用戶(hù)信息服務(wù)端記錄隨即刪除。這套設(shè)計(jì)同時(shí)滿(mǎn)足四個(gè)約束對(duì)應(yīng) 1_CloudBroker.md 的結(jié)果client secret 不離開(kāi) Cloudflare secret不落入 APK、不進(jìn)入請(qǐng)求體授權(quán)碼、token 和領(lǐng)取憑據(jù)不出現(xiàn)在瀏覽器回調(diào) URL 中回調(diào) URL 只攜帶transactionId與status等非敏感參數(shù)完成通知不產(chǎn)生 Worker 輪詢(xún)請(qǐng)求客戶(hù)端一次 start、一次 claim沒(méi)有狀態(tài)輪詢(xún)新接口不改動(dòng)舊客戶(hù)端使用的/market/v2/auth/github市場(chǎng)舊接口保持兼容。協(xié)議責(zé)任劃分3_Operit2Client.md 的協(xié)議責(zé)任進(jìn)一步明確了邊界Worker 持有 OAuth client secret、生成 PKCE 和處理 GitHub 回調(diào)客戶(hù)端不包含 client ID 或 client secretFlutter 市場(chǎng)頁(yè)不持有 OAuth HTTP、平臺(tái) Intent、EventChannel 或 loopback receiver只管理自己的可見(jiàn) WebView 導(dǎo)航客戶(hù)端不向 Worker 反復(fù)查詢(xún)授權(quán)狀態(tài)Rust 解析和校驗(yàn) Broker 響應(yīng)并以單元測(cè)試固定協(xié)議契約。客戶(hù)端協(xié)議實(shí)現(xiàn)Broker Service 與 Coordinator云端 Worker 的行為無(wú)法在本倉(cāng)庫(kù)直接查看后端位于獨(dú)立的marketWorker 工程但 Android 端的協(xié)議實(shí)現(xiàn)完整存在于本倉(cāng)庫(kù)可以直接對(duì)照。1.GitHubOAuthBrokerService協(xié)議的兩個(gè) HTTP 端點(diǎn)GitHubOAuthBrokerService.kt 是客戶(hù)端側(cè)與 Worker 通信的唯一入口基地址硬編碼為https://api.operit.app見(jiàn) L144。它封裝了兩個(gè)請(qǐng)求startLogin(completionRedirectUri)L60-L82POST$BROKER_BASE_URL/oauth/github/start請(qǐng)求體為{completionRedirectUri: ...}解析返回GitHubOAuthBrokerStartResponse該響應(yīng)攜帶transactionId、deliveryCredential、authorizationUrl、completionRedirectUri與expiresAtL18-L25。客戶(hù)端拿到后即可展示授權(quán)頁(yè)同時(shí)本地私有保存領(lǐng)取憑據(jù)claimLogin(transactionId, deliveryCredential)L84-L128POST$BROKER_BASE_URL/oauth/github/claim請(qǐng)求體為{transactionId: ..., deliveryCredential: ...}。響應(yīng)status必須為complete否則拒絕L108-L112隨后解出accessToken、tokenType、scope、expiresIn、refreshToken與userL113-L122。兩個(gè)請(qǐng)求都使用 30 秒超時(shí)的 OkHttpClientL49-L53錯(cuò)誤統(tǒng)一封裝為IllegalStateException并附帶 HTTP 狀態(tài)碼與響應(yīng)體便于排查L(zhǎng)130-L136。響應(yīng)解析使用ignoreUnknownKeys的寬松 Json 配置L55-L58保證前后端字段演進(jìn)時(shí)舊客戶(hù)端不因多余字段崩潰。2.GitHubOAuthCoordinator事務(wù)生命周期管理GitHubOAuthCoordinator.kt 是客戶(hù)端側(cè)的編排中樞startLogin(completionRedirectUri)L18-L33調(diào)用 Broker Service 創(chuàng)建事務(wù)并把transactionId、deliveryCredential、expiresAt通過(guò)GitHubAuthPreferences.saveActiveOAuthTransaction寫(xiě)入本地 DataStore隨后返回事務(wù)供 UI 展示授權(quán)頁(yè)completeLogin(completionUri)L35-L82先校驗(yàn)完成鏈接的transactionId與當(dāng)前活動(dòng)事務(wù)一致L39-L41否則直接失敗隨后按status參數(shù)分流——complete繼續(xù)領(lǐng)取、denied視為用戶(hù)取消并清事務(wù)、error透?jìng)麇e(cuò)誤信息、其余視為非法狀態(tài)確認(rèn)完成后調(diào)用claimLogin領(lǐng)取一次 token保存認(rèn)證信息并清除活動(dòng)事務(wù)cancelLogin()L84-L86用戶(hù)取消時(shí)清空活動(dòng)事務(wù)避免遺留憑據(jù)被復(fù)用。值得注意的細(xì)節(jié)內(nèi)嵌登錄使用固定完成地址https://api.operit.app/oauth/github/completeL90而外部瀏覽器登錄則使用 loopback 臨時(shí)地址見(jiàn)下文兩者都會(huì)在 start 時(shí)預(yù)注冊(cè)給 Worker對(duì)應(yīng)文檔中重定向到瀏覽器回調(diào) Host 預(yù)先注冊(cè)的完成地址。3. 事務(wù)憑據(jù)的持久化GitHubAuthPreferencesGitHubAuthPreferences.kt 基于 DataStoregithub_auth_preferencesL19-L20管理全部 GitHub 認(rèn)證狀態(tài)。與本次改造直接相關(guān)的設(shè)計(jì)有認(rèn)證版本門(mén)檻REQUIRED_AUTH_VERSION 3L43isAuthSessionCurrentL90-L94要求本地會(huì)話(huà)的auth_version 3且授予 scope 覆蓋notifications,public_repo,user:email,read:userL42舊版本認(rèn)證數(shù)據(jù)不會(huì)被新認(rèn)證代碼繼續(xù)使用呼應(yīng) 2_AndroidClient.md 的認(rèn)證版本升級(jí)舊 APK 數(shù)據(jù)不會(huì)被新版認(rèn)證代碼繼續(xù)使用活動(dòng)事務(wù)三鍵active_oauth_transaction_id、active_oauth_delivery_credential、active_oauth_expires_atL55-L57getActiveOAuthTransactionL245-L258在讀回時(shí)會(huì)檢查過(guò)期并自動(dòng)清除杜絕過(guò)期憑據(jù)被 claim領(lǐng)取后清理saveAuthInfoL131-L165在寫(xiě)入 token 的同時(shí)移除活動(dòng)事務(wù)三鍵與服務(wù)端記錄隨即刪除形成兩端對(duì)稱(chēng)的單次語(yǔ)義。Android 登錄 UI通用瀏覽器回調(diào)組件與雙路徑GitHubLoginDialog.kt 保留了內(nèi)嵌 WebView與外部瀏覽器兩條登錄路徑L50-L54 的GitHubLoginMode三態(tài)CHOOSER / EMBEDDED / EXTERNAL但底層機(jī)制全部替換。內(nèi)嵌路徑BrowserCallbackDialog通用組件內(nèi)嵌登錄不再持有 GitHub 協(xié)議邏輯而是復(fù)用通用組件 BrowserCallbackDialog.kt注釋明確寫(xiě)著 Presents one host-owned browser flow and reports navigation to its registered callback destination。它只負(fù)責(zé)三件事加載authorizationUrlL109-L111在shouldOverrideUrlLoading與onPageStarted兩個(gè)時(shí)機(jī)捕獲導(dǎo)航L79-L95用matchesCallbackDestinationL189-L194按scheme / host / port / path 四元組匹配完成地址命中即回調(diào)onCompletion(uri)并stopLoading()處理超時(shí)與釋放expiresAt到期未完成則回調(diào)onFailureL113-L127releaseBrowserCallbackWebViewL197-L208在釋放時(shí)依次執(zhí)行停止加載、about:blank、清歷史、移除視圖、destroy()避免 WebView 泄漏。由于完成地址是https://api.operit.app/oauth/github/complete這樣的 https 地址而非自定義 scheme2_AndroidClient.md 的結(jié)果清單中的三項(xiàng)隨之成立刪除舊自定義 scheme、外部瀏覽器回調(diào)和 Activity Intent 接管瀏覽器回調(diào)組件不包含 GitHub 協(xié)議、token 或領(lǐng)取憑據(jù)刪除 Android 的 client ID 與 client secret BuildConfig 字段。外部路徑一次性 loopback 接收器外部瀏覽器登錄使用 GitHubOAuthLoopbackCallbackServer.kt 在127.0.0.1上臨時(shí)監(jiān)聽(tīng)一個(gè)端口要求端口號(hào) 1024L98、L112-L114完成地址為http://127.0.0.1:port/oauth/github/completeL15-L22。awaitCompletionL24-L40只接受一次 GET 請(qǐng)求校驗(yàn)路徑與完成地址一致L63-L66把查詢(xún)參數(shù)拼接回完成 URI 后返回 200其余請(qǐng)求返回 404。整個(gè)流程在 GitHubLoginDialog.kt 的GitHubExternalLoginDialog中L240-L301用withTimeout(remainingMillis)包裹超時(shí)即按登錄失敗處理finally中關(guān)閉服務(wù)器并清理事務(wù)——這與文檔完成通知不產(chǎn)生 Worker 輪詢(xún)請(qǐng)求的約束一致因?yàn)檎麄€(gè)鏈路只有一次授權(quán)頁(yè)展示和一次完成回調(diào)。登出與會(huì)話(huà)隔離2_AndroidClient.md 還規(guī)定了一個(gè)易被忽略的體驗(yàn)細(xì)節(jié)用戶(hù)明確退出 GitHub 登錄時(shí)應(yīng)用會(huì)清除自身 WebView 的 Cookie 和 WebStorage再刪除本地認(rèn)證信息。下一次登錄不會(huì)靜默復(fù)用之前的 GitHub Web 會(huì)話(huà)這不會(huì)影響系統(tǒng)瀏覽器或 Chrome 的 GitHub 登錄狀態(tài)。即退出登錄需要同時(shí)清 WebView 會(huì)話(huà)與本地認(rèn)證數(shù)據(jù)但作用域嚴(yán)格限定在應(yīng)用自身 WebView避免誤傷系統(tǒng)瀏覽器中用戶(hù)已登錄的 GitHub 賬號(hào)。Operit 2 客戶(hù)端遷移類(lèi)型化服務(wù)替代命令字符串Operit 2Rust CLI 與 Flutter 市場(chǎng)的遷移方向與 Android 一致但多了一個(gè)架構(gòu)約束3_Operit2Client.md 要求兩端都調(diào)用 Core 的類(lèi)型化GitHubOAuthBrokerServiceFlutter 使用生成的 Dart proxy、CLI 使用生成的 Rust proxy兩者都不傳遞市場(chǎng)認(rèn)證命令字符串、不解析命令 stdout也不手寫(xiě) CoreLink 請(qǐng)求。具體分工應(yīng)用自己準(zhǔn)備完成地址Core 用該地址調(diào)用 Worker 的/oauth/github/start并私有保存 delivery credential應(yīng)用展示授權(quán)頁(yè)并交回完成鏈接——CLI 使用臨時(shí) loopback 并在終端打印授權(quán)鏈接Flutter 市場(chǎng)登錄對(duì)話(huà)框攔截其 WebView 的完成導(dǎo)航Core 只在收到與當(dāng)前事務(wù)、目標(biāo)地址都匹配的完成鏈接后 claim 一次并保存 Worker 返回的 GitHub tokenRust 側(cè)通過(guò)單元測(cè)試固定協(xié)議契約Rust 解析和校驗(yàn) Broker 響應(yīng)并以單元測(cè)試固定協(xié)議契約。這條遷移路徑的意圖很清晰舊實(shí)現(xiàn)里 CLI 依賴(lài)環(huán)境變量 client ID、Flutter 要求用戶(hù)粘貼 token、兩端以命令字符串與 stdout 解析方式對(duì)接市場(chǎng)認(rèn)證都屬于憑據(jù)或協(xié)議細(xì)節(jié)外泄的脆弱設(shè)計(jì)新實(shí)現(xiàn)把協(xié)議收斂為類(lèi)型化的 start/claim 調(diào)用憑據(jù)只在 Core 與 Worker 之間傳遞。結(jié)果清單與安全收益匯總綜合 1_CloudBroker.md 與 2_AndroidClient.md 的結(jié)果章節(jié)本次改造的驗(yàn)收標(biāo)準(zhǔn)如下client secret 不離開(kāi) Cloudflare secret任何客戶(hù)端二進(jìn)制與請(qǐng)求體中都不可見(jiàn)授權(quán)碼、token 和領(lǐng)取憑據(jù)不出現(xiàn)在瀏覽器回調(diào) URL 中回調(diào) URL 僅攜帶事務(wù) ID 與狀態(tài)完成通知不產(chǎn)生 Worker 輪詢(xún)請(qǐng)求全鏈路僅 start / claim 兩次 HTTP 往返新接口不改動(dòng)舊客戶(hù)端使用的/market/v2/auth/github舊接口保持兼容直至遷移截止日Android 刪除舊自定義 scheme、外部瀏覽器回調(diào)和 Activity Intent 接管瀏覽器回調(diào)組件不含 GitHub 協(xié)議、token 或領(lǐng)取憑據(jù)Android 刪除 client ID 與 client secret 的 BuildConfig 字段認(rèn)證版本升級(jí)到 3舊 APK 數(shù)據(jù)不會(huì)被新認(rèn)證代碼繼續(xù)使用。從實(shí)現(xiàn)事實(shí)看這些收益都能在本倉(cāng)庫(kù)的源碼中得到印證GitHubOAuthBrokerService的請(qǐng)求體只有completionRedirectUri/transactionId/deliveryCredentialGitHubOAuthBrokerService.kt沒(méi)有任何 client secret 字段完成鏈接校驗(yàn)依賴(lài)transactionId與狀態(tài)參數(shù)GitHubOAuthCoordinator.kt倉(cāng)庫(kù)中已搜不到operit://github-oauth-callback或 client secret BuildConfig 字段的殘留。部署順序與現(xiàn)狀index.md 的狀態(tài)章節(jié)記錄了該改造的推進(jìn)節(jié)奏云端密鑰已配置、新 OAuth App 已創(chuàng)建、D1 遷移已應(yīng)用Worker 部署待后端現(xiàn)有未提交市場(chǎng)改動(dòng)整理后執(zhí)行Operit 2 客戶(hù)端遷移進(jìn)行中CLI 包當(dāng)時(shí)的編譯問(wèn)題與本協(xié)議無(wú)關(guān)。部署順序明確為后端先于 Android——這是合理的依賴(lài)順序新 APK 依賴(lài) Worker 的/oauth/github/start與/oauth/github/claim端點(diǎn)云端必須先就緒舊客戶(hù)端才能繼續(xù)使用原 OAuth App 平穩(wěn)過(guò)渡到遷移截止日。對(duì)讀者而言若要在自己的項(xiàng)目里復(fù)刻這套方案最小可復(fù)制的骨架是一臺(tái)持有 client secret 的云端 Worker負(fù)責(zé) PKCE 與授權(quán)碼交換、一個(gè)短期事務(wù)存儲(chǔ)帶過(guò)期與單次領(lǐng)取語(yǔ)義、客戶(hù)端側(cè)一次 start 一次 claim 的類(lèi)型化調(diào)用以及一個(gè)只按 scheme/host/port/path 匹配完成地址的通用瀏覽器回調(diào)組件。Operit 的 GitHubOAuthBrokerService.kt 與 GitHubOAuthCoordinator.kt 提供了現(xiàn)成的參考實(shí)現(xiàn)。贊分享AI Agent人工智能大模型AI 應(yīng)用工具調(diào)用本地部署MCP ClientsAgent 記憶【免費(fèi)下載鏈接】OperitThe most powerful AI agent and AI chat software on Android/Operit是一款A(yù)ndroid上能力最為強(qiáng)大、發(fā)展最久的AI Agent項(xiàng)目地址https://gitcode.com/gh_mirrors/op/Operit點(diǎn)擊查看免費(fèi)下載相關(guān)推薦Operit GitHub OAuth 完成回調(diào)協(xié)議Worker 事務(wù)、應(yīng)用自持瀏覽器與一次性 Claim 的完整交付鏈路Operit GitHub OAuth 完成回調(diào)協(xié)議Worker 事務(wù)、應(yīng)用自持瀏覽器與一次性 Claim 的完整交付鏈路 本指南圍繞 Operit 倉(cāng)庫(kù)中AI Agent人工智能大模型AI 應(yīng)用工具調(diào)用本地部署MCP ClientsAgent 記憶GUI 自動(dòng)化MCP Toolbox 之 oceanbase-execute-sql在 OceanBase 上執(zhí)行 SQL 的 MCP 工具配置與實(shí)戰(zhàn)指南MCP Toolbox 之 oceanbase execute sql在 OceanBase 上執(zhí)行 SQL 的 MCP 工具配置與實(shí)戰(zhàn)指南 oceanbasAI Agent人工智能大模型AI 應(yīng)用工具調(diào)用本地部署MCP ClientsAgent 記憶GUI 自動(dòng)化Open edX 認(rèn)證憑證交換實(shí)戰(zhàn)auth_exchange 模塊的第三方 OAuth 接入與 Token 登錄實(shí)現(xiàn)Open edX 認(rèn)證憑證交換實(shí)戰(zhàn)auth_exchange 模塊的第三方 OAuth 接入與 Token 登錄實(shí)現(xiàn) 導(dǎo)讀 本文以 Open edX 平臺(tái)后端教育上一篇深入解析mshumer/gpt-author項(xiàng)目AI自動(dòng)生成小說(shuō)全流程指南下一篇從0到1使用Google Workspace MCP Server構(gòu)建自動(dòng)化郵件處理系統(tǒng)創(chuàng)作聲明:本文部分內(nèi)容由AI輔助生成(AIGC),僅供參考