Skip to content
← 文章目次 HCYTLOG / 文章與筆記

OAuth 解除綁定的取捨:那些看起來很小、其實很難的決定

保留 Google 解除綁定的開發經過,補上站內 session、OAuth scope、access/refresh token 與登入同意撤銷的差異。

本文目錄

2026-10-08 更新:保留當時拿掉 revoke 呼叫、調整登出確認的經過,補上站內帳號、session 與 Google 授權的差異。這是技術說明的更正,沒有重新稽核目前產品,也不據此判定現在存在漏洞。

當時怎麼處理解除綁定

早上打開昨天調完的 UI,間距問題總算舒服多了。從 Clarity session 看,滑動速度降了一些,心情小小被安慰。結果沒高興多久,Google OAuth 解除綁定流程又跳出來提醒我:直覺跟安全,要怎麼一起兼顧?

一開始以為不難:按個按鈕、呼叫 API、revoke token,收工。真的動手後才發現,OAuth 的麻煩全在細節。當時我曾寫了撤銷 Google token 的邏輯,後來為了減少 API 呼叫與錯誤處理,把它拿掉,改成只更新系統內的綁定狀態。我那時用「access token 會過期」來解釋這個取捨,現在回頭看,這個理由漏掉了授權同意與 refresh token,下面會補清楚。

接著遇到更棘手的情境:使用者是用 Google 註冊的,解除綁定之後怎麼處理登入?系統裡根本沒有他的密碼,會不會變成幽靈帳號?我第一個反應是「那就登出所有裝置吧」。寫完又覺得太暴力:有些人只是要換綁定的 Google 帳號,被迫全部重登體驗很差。最後我在 UI 補了一道確認步驟,跳出對話框清楚告訴他「解除綁定後會登出所有裝置」,請他確認。雖然多了一步,但至少把預期講清楚,避免突然被踢出而困惑。

2026 更新:解除關聯不等於撤銷授權

站內帳號與 session 是自己的責任

「這個 Google 帳號可以登入哪個會員」是站內帳號關聯;「哪些裝置仍維持登入」是自己的 session。解除登入關聯,不會自動清掉既有 session;登出所有裝置,也不會撤銷 Google 端的授權。兩邊都要有明確的處置。

若 Google 是唯一登入方式,先要求使用者建立另一種可用的登入方式,再讓他解除綁定。只告知會登出,沒有解決「之後如何登入」。是否讓全部裝置登出,可以依產品風險決定,但確認視窗要把實際行為講清楚。

scope、access token 與 refresh token 要分開

OAuth scope 描述應用程式獲准存取什麼資料或功能。access token 到期,只代表那一枚憑證不能再用;若仍持有有效的 refresh token,還可能取得新的 access token。因此「等到期」不能直接取代撤銷存取權限。

Google OAuth 2.0 政策要求在不再需要存取使用者帳號或權限時撤銷 token,並從系統永久刪除。真正的解除授權流程要處理供應商端的撤銷、自己儲存的憑證,以及撤銷失敗時的重試與狀態,不能只翻轉一個站內欄位。

只用 Google 登入,也有同意分享的狀態

Google Sign-In 用 ID token 表達身分,和呼叫 Google API 的 OAuth access token 用途不同。Google 的 ID token 撤銷說明提供 google.accounts.id.revoke,用來撤銷分享身分資料的同意;這個方法不能拿來管理 OAuth 2.0 authorization scopes。

實作前先確認自己使用哪套登入/授權流程、取得哪些 token,才知道該呼叫哪個撤銷方法。也讓使用者看懂:他是在解除站內登入關聯、撤銷 Google 的資料分享,還是在刪除整個會員帳號。

同一次開發,還調整了哪些會員體驗

Email 欄位與分析工具

把 OAuth 的邏輯整理完,順勢把會員相關說明也更新,省得之後自己回頭看滿頭問號。EmailChangeForm 也做了控管:當時這個產品以提供者的 email 作為帳號資料,所以對 Google 登入使用者隱藏修改入口。這是當時的帳號設計,不代表所有 OAuth 產品都不能提供獨立的聯絡信箱。

下午看 Clarity 跟 Google Analytics 的載入方式,想到 GDPR 的 Cookie Consent 一直沒好好收。之前是直接載入分析工具,根本不管同意狀態;長期下來有法規風險。於是把 CookieConsent 與 MicrosoftClarity 改成動態載入,GoogleAnalytics 則依 consent 決定是否放分析 Cookie。說不定很多人不在意,但這種小洞不補,最後要嘛變技術債,要嘛造成流失。

另外也把 Navbar 的效能債清了一輪。Clarity 看到點擊有卡頓,打開原始碼才發現先前 debug 留了一堆 console.log。清掉後順手用 useCallback 包事件處理,避免每次渲染都生新的函式。這種不起眼的小調整,行動裝置上的反應其實差很多。

刪除帳號與鍵盤操作

帳號刪除流程也被迫改設計。原本要輸入密碼確認,但 OAuth 使用者壓根沒有密碼。於是拿掉密碼確認,改成靜默拿一次 Google token 驗證身分,刪除流程更合理、也更直覺。

這裡記錄的是當時的實作。若現在要設計刪除流程,我會另確認這次身分驗證是否足夠、session 是否過舊,以及取消或驗證失敗能否安全返回;靜默取得 token 不等於使用者剛剛重新驗證過身分。

也把解除綁定按鈕從 onClick 改成原生 form submit,讓鍵盤操作與其他無障礙操作更方便。看起來只是微調,但產品成熟之後,這些細節反而會變得重要。

回頭看這兩天,很多 commit 都是在刪東西。移除呼叫可以減少延遲和維護成本,但先要確認原本那個呼叫負責哪一層狀態。少做一步之前,把帳號、session 和授權的責任拆清楚,才知道自己省掉的是多餘工作,還是必要的安全處理。