Google Sheets OAuth:從授權到驗證的安全路徑
以完全虛構的桌面應用程式為例,先看資料如何流動、敏感資訊停在哪裡,再依最短路徑完成設定與排錯。
成功不是「看到同意畫面」
完成的判準是:應用程式以最小 scopes 取得憑證、安全儲存,重啟後仍能載入,並對一份明確選定的測試試算表完成唯讀 metadata request。瀏覽器 redirect 與 authorization code 都只是短暫中間狀態。
雲端
示範專案已啟用 Google Drive API 與 Google Sheets API,OAuth client 類型為 Desktop app。
本機
程式可短暫監聽 loopback callback,token 使用作業系統憑證庫或權限受限的本機檔案。
OAuth Authorization Code Sequence
這張圖只回答一個問題:授權碼如何從使用者同意,安全走到本機 token store?
從 App 到 Token Store 的完整序列
結論:localhost redirect 是 Desktop OAuth 的交接點,不是部署失敗;App 必須先啟動 listener,驗證 state,再交換一次性 code。
Trust Boundary
這張圖只回答一個問題:敏感資訊應停在哪個信任區域,哪些資料才能進入 Sheet?
Browser、Google OAuth Server 與 Local Application
結論:client secret 與 token 只留在 Local Application;authorization code 短期存在、用後即棄;Sheet、Git 與聊天都不是憑證儲存區。
先固定最小 scopes
https://www.googleapis.com/auth/drive.file只讓應用程式接觸由它建立、或使用者明確選取的檔案。
https://www.googleapis.com/auth/spreadsheets需要讀寫內容時使用;若驗證只讀,可評估 https://www.googleapis.com/auth/spreadsheets.readonly。
7 個可驗證步驟
流程圖已說明資料移動;這裡只保留每一步的操作與完成條件。
啟用 Drive 與 Sheets API
成果:API Library 顯示兩個服務均為 Enabled。
在示範 project 啟用兩個 API。建立 OAuth client 不會自動啟用服務。
gcloud services enable \
drive.googleapis.com \
sheets.googleapis.com \
--project=YOUR_DEMO_PROJECT_ID設定 Testing audience
成果:受控測試帳號已列入 Test users。
保持 Publishing status 為 Testing。這裡控制「誰能測試」,不是控制 scope。
建立 Desktop OAuth client
成果:client 設定只存在私密本機環境。
# OAuth 設定與 token 永不進版控
client_secret*.json
.google/
.app/google_token.json產生授權網址並啟動 listener
成果:Browser 顯示預期 scopes,redirect 指向 loopback。
每次建立新的 state 與 PKCE verifier;先啟動 listener,再開啟 authorization URL。
export OAUTH_CLIENT_ID='YOUR_CLIENT_ID'
export OAUTH_REDIRECT_URI='http://127.0.0.1:YOUR_PORT/callback'
export OAUTH_SCOPES='https://www.googleapis.com/auth/drive.file https://www.googleapis.com/auth/spreadsheets'交換一次性 code
成果:Token Endpoint 成功回應,log 只記錄狀態。
由 OAuth library 在記憶體中送出 code、同一 redirect URI 與 PKCE verifier。code 不落盤、不重送。
POST https://oauth2.googleapis.com/token
Content-Type: application/x-www-form-urlencoded
client_id=YOUR_CLIENT_ID&code=ONE_TIME_CODE&
code_verifier=YOUR_PKCE_VERIFIER&
redirect_uri=http%3A%2F%2F127.0.0.1%3AYOUR_PORT%2Fcallback&
grant_type=authorization_code安全儲存 token
成果:只有目前使用者可讀取 token store。
優先使用作業系統憑證庫。若示範必須使用檔案,目錄設為 700、檔案設為 600,並採原子寫入。
install -d -m 700 ~/.app
install -m 600 /dev/null ~/.app/google_token.json
# 由應用程式原子寫入,不在命令列輸入 token重啟並驗證最小 request
成果:新 process 載入憑證並讀取指定試算表 metadata。
以 OAuth library 載入或 refresh credential,再讀取明確選定的測試 spreadsheet。公開輸出仍使用 placeholder。
GET https://sheets.googleapis.com/v4/spreadsheets/YOUR_TEST_SPREADSHEET_ID?fields=spreadsheetId%2Cproperties.title
Authorization: Bearer [ACCESS_CREDENTIAL_PLACEHOLDER]Troubleshooting Decision Tree
這張圖只回答一個問題:看到授權或 API 錯誤時,第一個正確修復動作是什麼?
依症狀選擇最短修復路徑
結論:先把錯誤分類,再做最小修正。localhost 無法顯示頁面不必然代表授權失敗;保留完整 URL,讓仍在執行的 App 接手。
完成前逐項驗證
- OAuth client 類型是 Desktop app;consent screen 維持 Testing。
- authorization URL 只有最小 scopes,state 與 PKCE 每次重新產生。
- localhost listener 綁定 loopback、驗證 state,接受一次後關閉。
- authorization code 不落盤、不重用,也不進 log。
- client 設定與 token 僅留在本機安全儲存;不進 Sheet、Git 或聊天。
- 重啟 process 後仍可載入或 refresh credential。
- 最小 request 只讀取明確選定的測試 spreadsheet metadata。
- 公開內容只使用 placeholder,沒有帳號、真實 credential、token 或內部資訊。