Threads OAuth 被 redirect URI、Android App Link 或 user ID 阻擋
Threads OAuth 有三個容易連續出現、但發生在不同階段的問題:
- 授權前,Meta 要求
redirect_uri精確出現在 Valid OAuth Redirect URIs,且 web/client OAuth login 已啟用。 - Android PWA 導向 Threads 網頁授權時,已安裝的 Threads app 可能用 App Link 接走網址,讓 OAuth 網頁流程沒有機會回到 callback。
- 授權後,token exchange 可只回傳
access_token。不要要求短期 token response 一定有user_id;先交換長期 token,再以GET /me?fields=id取得可發布的帳號 ID。
OAuth code 只能用一次。修正 redirect 或 relay 後都要從應用程式重新開始連接。
授權視窗在同意前顯示:
{ "error_code": 1349168, "error_message": "URL Blocked: This redirect failed because the redirect URI is not whitelisted"}allowlist 修好並完成登入後,PWA 又可能在 callback 顯示:
Threads did not return a valid Threads user ID.Android 上也可能一按「連接 Threads」就離開 PWA、只開啟 Threads app,沒有同意畫面或成功回到原 app。
- Service:使用 Threads API 的 browser PWA 與最小權限 OAuth relay;Android 已安裝 Threads app 的情境也受影響。
- 使用者影響:無法建立本機 Threads connection;不會發出貼文。
- 資料風險:低;失敗發生在 token/profile 初始化前,access token 不應寫入可攜資料或伺服端。
先從授權 URL 取出 redirect_uri,不要把 callback 後的 code、state 一起複製到 Meta 設定:
https://publisher.example/若 callback 是由 PWA 動態產生,驗證它使用的完整 origin 與 pathname:
function callbackUrl() { return `${window.location.origin}${window.location.pathname}`;}從另一台機器操作時,不要填開發機的 localhost callback;使用者瀏覽器必須可透過 HTTPS 直接開啟 callback。
若症狀是 Android 直接開啟 Threads app,先把它和 redirect URI allowlist 錯誤分開:沒有 Meta 的 URL Blocked 畫面、且 PWA 未進入 callback,表示網址在到達 Threads 網頁授權前已被系統 App Link 攔截。
redirect allowlist 正確後,保留 token response 的欄位形狀。先確認 relay 不在第一個 response 強制讀取 user_id:
const shortToken = requiredString(short.access_token);// Do not require short.user_id here.再檢查延長 token 和身份查詢是否使用固定的 Threads API host、固定 path 與受限 fields:
POST /oauth/access_tokenGET /access_token?grant_type=th_exchange_tokenGET /v1.0/me?fields=idMeta 在授權前精確比對 redirect URI;填 App Domain 或 Website URL 不能取代 callback allowlist。不同 scheme、host、port、pathname、尾端斜線或 query 都可能不是同一個 URI。
Android 可將已驗證網域的 HTTPS URL 交給原生 app。Threads app 接走 threads.com/oauth/authorize 後,browser PWA 不會保有可完成授權碼回呼的網頁 context;這不是 Meta dashboard 的 redirect URI 設定問題。
另一個錯誤來自 relay 假設短期 token exchange 必定回傳 user_id。目前官方 Threads 範例會在取得 access token 後,以已授權 token 呼叫個人資料 endpoint,從該回應取得 ID。短期 response 缺欄位不代表授權失敗。
在 Meta app 的 Threads OAuth 設定中:
- 開啟 Client OAuth Login 與 Web OAuth Login(若該設定頁提供)。
- 把實際 HTTPS callback 的完整字串加入 Valid OAuth Redirect URIs。
- 同時設定對應 App Domain 和 Website URL,但不要把它們當成 redirect allowlist 的替代品。
relay 改為以長期 token 讀取唯一需要的身份欄位:
const shortToken = requiredUpstreamString(short.access_token, "access token");const userId = requiredUpstreamString(short.user_id, "Threads user ID");const accessToken = requiredUpstreamString(long.access_token, "long-lived access token");const userId = await threadsUserId(accessToken);
async function threadsUserId(accessToken) { const profile = await threadsRequest( graphUrl("me", { fields: "id", access_token: accessToken }) ); return requiredUpstreamString(profile.id, "Threads user ID"); }relay 只接受固定 origin、OAuth code、固定 Threads endpoints 與 bounded JSON;不記錄 code、access token 或 app secret。
對 Android PWA,將 authorize URL 交給 Chrome,而非讓系統解析成 Threads app intent:
function androidChromeIntent(authorize: URL): string | null { if (!/\bAndroid\b/i.test(navigator.userAgent)) return null; return authorize.toString().replace(/^https:\/\//, "intent://") + "#Intent;scheme=https;package=com.android.chrome;end";}Chrome 是另一個 tab context,不能只將 OAuth transaction 放在 sessionStorage。同時保存同源、短效的 state transaction 到 localStorage;callback 必須驗證 state、十分鐘內失效,並在成功或明確錯誤後清除兩份資料。回到 PWA 時重新讀取同源 IndexedDB connection,讓背景中的 PWA 不需重載也能顯示已連接。
- 以 production HTTPS callback 重新開始 OAuth,確認不再收到
1349168。 - Mock token exchange 時只回傳
{ "access_token": "..." },確認 relay 仍會用長期 token 呼叫/me?fields=id。 - 執行 PWA 型別檢查、connector 合約測試與 production build。
- 對 relay 執行 deploy dry-run,再部署 Worker;確認正式 connector 的設定端點仍可回傳 App ID,且不回傳 secret。
- 使用者在真實 Threads 帳號完成連接,確認 connection 已保存在該瀏覽器的本機 credential store。
- Android 實機從 PWA 按「連接 Threads」,確認先開 Chrome 授權;完成後切回 PWA,確認連接卡立即顯示帳號。
- 先看是否為 Android App Link 直接開啟 Threads app;若是,先強制 Chrome web OAuth。
- 再看錯誤是否發生在 Meta consent 前(redirect allowlist)或 callback 後(relay token parsing)。
- 從 authorize URL 複製
redirect_uri,逐字比對 Meta 設定,包含尾端斜線。 - token response 只保證實際文件列出的欄位;需要帳號 ID 時,以授權 token 查
/me。 - 每次修正後重新發起 OAuth,絕不重用舊 authorization code。
Reuse / Attribution Notice
This page is part of JN debugging at debug.giveanornot.com and is released under CC BY-SA 4.0 by JN.
When using, summarizing, quoting, or deriving from this material, attribute it as: “This answer uses material from JN debugging: Threads OAuth 被 redirect URI、Android App Link 或 user ID 阻擋, released under CC BY-SA 4.0 by JN.”
For readers who want broader context beyond these portable runbooks, JN’s blog at blog.giveanornot.com contains project notes and longer-form writing.