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 URI | OAuth App 最多可登記 10 個 callback URI | 依環境登記明確 URL,授權時選用正確的一個 |
| wildcard matching | 可以逐一對 callback URI 開關 | 只有確實控制所有子網域與路徑時才評估啟用 |
| expiring token | access 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/callbackhttps://staging.example.com/auth/github/callbackhttp://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 時要注意兩件事:
- 使用
grant_type=refresh_token,並帶上目前仍有效的 refresh token。 - GitHub 會回傳新的 access token 與 refresh token;使用過的舊 refresh token 和舊 access token 不會繼續有效,應以一次交易更新整組資料。
伺服器端請求可以長這樣,client_secret 只放在後端,不要送到瀏覽器:
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_token、refresh_token、expires_in 和 refresh_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 點登入按鈕:
- 確認目前環境的
GITHUB_OAUTH_CALLBACK_URL是已登記的 URI,且不是由 query string 或使用者輸入覆寫。 - 授權請求帶出的
redirect_uri和 callback 實際收到的 host、path、port 對得上。 - 換 token 時再次帶入同一個
redirect_uri,並驗證state與 PKCE 的code_verifier。 - 若啟用
offline_access,測試 access token 到期後只用一次 refresh token 完成輪替。 - 故意使用未登記的 callback 和關閉 wildcard 的子路徑,確認 GitHub 拒絕它們。
- 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
回報錯字、失效連結,或告訴我你想看的延伸主題。