1791 字
9 分鐘

GitHub OAuth App 多個 callback URL 怎麼設?一起檢查 wildcard 與 refresh token

如果同一個 GitHub OAuth App 同時服務本機、staging 和 production,過去常要在一個 callback URL 上想辦法共用環境。GitHub 在 2026 年 8 月 14 日新增多個 redirect URI、每個 URI 的 wildcard 控制,以及 OAuth App 的短效 access token 與 refresh token。

直接結論是:先把每個環境的 callback URL 登記清楚,再讓授權請求和換 token 請求使用同一個 redirect_uri;如果啟用 expiring token,必須把 refresh token 的輪替寫回安全儲存。 多一個輸入欄位,不代表可以放寬 callback 驗證。

這次 OAuth App 多了哪些設定#

GitHub Changelog 把這次更新拆成三個互相獨立的能力。先分開理解,才不會只因為想支援 staging 就順手打開 wildcard 或 token rotation。

能力目前的行為對應的實作工作
多個 redirect URIOAuth App 最多可登記 10 個 callback URI依環境登記明確 URL,授權時選用正確的一個
wildcard matching可以逐一對 callback URI 開關只有確實控制所有子網域與路徑時才評估啟用
expiring tokenaccess token 有效 8 小時,refresh token 不使用時有效 6 個月保存新的 token pair,過期或失效時導回授權流程

GitHub 文件把 OAuth App 的 callback URL 稱為 redirect URL。這和 GitHub App 的 user authorization callback URL 是不同設定,不要只搜尋其中一種文件就當成兩者完全相同。

先依環境登記 callback URL#

在 OAuth App 設定頁使用 Add redirect URI,把真正會接收授權 code 的路徑逐一加進去。例如:

https://app.example.com/auth/github/callback
https://staging.example.com/auth/github/callback
http://127.0.0.1:4321/auth/github/callback

本機桌面或 CLI 工具需要 loopback callback 時,GitHub 文件建議使用 127.0.0.1::1,而不是把 localhost 當成所有環境共用的替代值。port 可以由程式在授權請求中帶入,但 path 和 host 仍要符合文件規則。

應用程式產生授權網址時,將 callback URI 視為目前部署環境的設定,而不是從使用者輸入直接拼接:

const callbackUrl = process.env.GITHUB_OAUTH_CALLBACK_URL;
if (!callbackUrl) {
throw new Error('GITHUB_OAUTH_CALLBACK_URL is not configured');
}
const authorizeUrl = new URL('https://github.com/login/oauth/authorize');
authorizeUrl.search = new URLSearchParams({
client_id: process.env.GITHUB_CLIENT_ID ?? '',
redirect_uri: callbackUrl,
scope: 'read:user user:email offline_access',
state,
code_challenge: codeChallenge,
code_challenge_method: 'S256',
}).toString();

state 用來防止 CSRF,PKCE 的 code_challenge 和之後的 code_verifier 則要成對保存。它們不是這次新增功能,但多環境 callback 上線時正好要一起重新驗證;不要因為新增 URI 就省略原本的保護。

wildcard 要先關掉,還是可以直接開?#

當 wildcard matching 關閉時,GitHub 會要求 redirect URL 精確符合已登記的 callback URL。這是比較容易審查的預設邊界:staging 不會因為和 production 共用網域片段,就自動取得另一個路徑的授權 code。

wildcard 適合的情境很窄,例如你確實控制所有 tenant subdomain,而且每個相關 path 都有同樣的路由與權限保護。GitHub 文件提醒,wildcard 會讓 authorization code 被送到 callback URL 底下符合規則的子網域或子路徑;如果那些位置會託管使用者內容,就可能放大 redirect 風險。

還有一個既有 App 的檢查點:GitHub Changelog 說明,只有一個 redirect URI 的 App 可能保留過去的 wildcard 行為;文件也特別指出,2026 年 8 月 3 日以前設定單一 callback 的 App,其 wildcard 可能已經開啟。打開每個 OAuth App 的設定,逐一確認這個開關;不需要時主動關閉,不要把歷史行為當成目前的安全決策。

啟用短效 token 後,換 token 請求怎麼接#

GitHub 的 expiring access token 會在八小時後失效,refresh token 在六個月沒有使用時失效。想逐步導入時,可以在授權請求加入 offline_access,先讓單次登入拿到短效 token 和 refresh token,再觀察現有 SDK 與儲存層是否支援。

換 token 時要注意兩件事:

  1. 使用 grant_type=refresh_token,並帶上目前仍有效的 refresh token。
  2. GitHub 會回傳新的 access token 與 refresh token;使用過的舊 refresh token 和舊 access token 不會繼續有效,應以一次交易更新整組資料。

伺服器端請求可以長這樣,client_secret 只放在後端,不要送到瀏覽器:

Terminal window
curl --fail-with-body --silent --show-error \
--request POST \
--url https://github.com/login/oauth/access_token \
--header 'Accept: application/json' \
--data-urlencode "client_id=$GITHUB_CLIENT_ID" \
--data-urlencode "client_secret=$GITHUB_CLIENT_SECRET" \
--data-urlencode 'grant_type=refresh_token' \
--data-urlencode "refresh_token=$GITHUB_REFRESH_TOKEN"

回應中的 access_tokenrefresh_tokenexpires_inrefresh_token_expires_in 應一起保存。若多個 request 可能同時發現 access token 過期,先在儲存層做 refresh lock 或版本檢查,避免兩個 request 同時消耗同一個 refresh token。

如果 GitHub 回傳 bad_refresh_token,不要無限重試;文件的處理方式是讓使用者重新走 web application flow 或 device flow,取得新的 token pair。這也表示 refresh token 不是永久登入憑證,資料庫應保留重新授權的狀態。

部署前用同一條流程驗證#

多個 callback URI 上線前,我會用每個環境各跑一次的方式檢查,而不是只在 production 點登入按鈕:

  1. 確認目前環境的 GITHUB_OAUTH_CALLBACK_URL 是已登記的 URI,且不是由 query string 或使用者輸入覆寫。
  2. 授權請求帶出的 redirect_uri 和 callback 實際收到的 host、path、port 對得上。
  3. 換 token 時再次帶入同一個 redirect_uri,並驗證 state 與 PKCE 的 code_verifier
  4. 若啟用 offline_access,測試 access token 到期後只用一次 refresh token 完成輪替。
  5. 故意使用未登記的 callback 和關閉 wildcard 的子路徑,確認 GitHub 拒絕它們。
  6. Business/Enterprise 環境另外確認管理員政策,避免把 OAuth App 的 callback 問題誤判成組織權限問題。

如果你的整合不只是「讓使用者登入」,還要以 App 身分存取 repository,建議另外比較 GitHub App 的細緻權限模型;可以接著看GitHub Agent Apps 上線前的安裝、權限與審核清單,但不要把 Agent App、GitHub App 和 OAuth App 的設定混在同一張表裡。

這次要記住的事#

多個 callback URI 解決的是環境與部署入口,不是把 redirect 驗證變寬。先用精確 URI 支援 dev、staging 和 production,再視資料流與網域控制能力判斷 wildcard;若啟用短效 token,就把 refresh token 視為會輪替的秘密值,讓重新授權成為明確的失敗路徑。

常見問題#

Q: 同一個 OAuth App 可以同時支援本機、staging 和 production 嗎?#

A: 可以。GitHub 目前允許 OAuth App 登記最多 10 個 redirect URI,授權時由 redirect_uri 指定這次使用的 callback。每個環境仍應使用固定設定,並在測試時確認未登記的 URL 不會被接受。

Q: 設定頁加了多個 callback URL,授權請求還要帶 redirect_uri 嗎?#

A: 建議要帶。GitHub 文件將 redirect_uri 列為授權請求與換 token 請求的重要參數;兩次使用同一個實際 callback,能讓 GitHub 比對授權 code 原本的去向,也避免多個環境互相混用。

Q: refresh token 失效後,可以用舊 access token 再換一次嗎?#

A: 不能把它當成可靠的回復方式。refresh token 過期或無效時,GitHub 會回傳 bad_refresh_token;應讓使用者重新授權。每次成功 refresh 後也要保存回傳的新 token pair,因為舊 pair 不會繼續有效。

參考資料:

GitHub Changelog:Multiple redirect URIs and token refresh for OAuth apps

GitHub Docs:Authorizing OAuth apps

GitHub Docs:Creating an OAuth app

GitHub OAuth App 多個 callback URL 怎麼設?一起檢查 wildcard 與 refresh token
https://laplusda.com/posts/github-oauth-app-redirect-uri-refresh-token/
作者
Zero
發佈於
2026-08-16
許可協議
CC BY-NC-SA 4.0
這篇文章有幫助嗎?

回報錯字、失效連結,或告訴我你想看的延伸主題。