登入、電子郵件與 OAuth
沿著登入狀態、電子郵件驗證、郵件寄送和 OAuth 回呼,定位驗證問題。
登入失敗、收不到郵件和第三方回呼失敗,經常出現在同一流程中,但處理方式不同。先在瀏覽器 Network 中找出失敗的請求,再查看回應本文和伺服器端記錄,不要只根據頁面最後重新導向的位置判斷。
一、登入後又回到登入頁面
依序觀察兩個請求:登入請求的回應,以及登入後第一個需要身分驗證的請求。
- 登入回應是否成功,是否要求繼續驗證電子郵件或雙重驗證碼。
- 回應是否設定 Cookie,瀏覽器是否提示該 Cookie 遭拒。
- 後續請求是否帶上 Cookie,Host 和通訊協定是否與登入時一致。
- 伺服器端是否能讀取有效的工作階段,使用者狀態是否仍允許存取。
本機請勿混用 localhost 和 127.0.0.1,正式環境也不要從 workers.dev 登入後,轉至自訂網域繼續驗證。不同主機的 Cookie 不會自動共用。
正式環境還要核對 VITE_SITE_URL、HTTPS、Proxy 是否改寫回應標頭,以及瀏覽器是否實際存取了舊網站。若懷疑舊 Cookie 影響結果,可以使用新的瀏覽器工作階段重現,但不能只以「清除 Cookie 後就好了」取代查明網域或工作階段問題。
請勿將 Cookie 原始值複製到記錄或客服案件。需要關聯工作階段時,在受控環境中檢查 saas_session 的有效期限和對應使用者即可。
二、區分 401、403 和 429
| 狀態 | 常見方向 | 還需要查看什麼 |
|---|---|---|
| 401 | 缺少有效的登入工作階段或權杖 | Cookie、工作階段有效期限、介面使用的驗證方式 |
| 403 | 驗證狀態、權限、帳號狀態或請求來源不符合要求 | code、error、details、中介軟體記錄 |
| 429 | 請求受到流量限制 | 觸發的操作、回應中的等待資訊、流量限制設定 |
403 不一定表示訪客已登入。有些請求在進入業務處理前,就會因來源或安全性原則而遭拒,也不能將所有 403 都視為缺少管理員角色。
如果回應包含 details.redirectUrl,先查看它要求進入的是電子郵件驗證、雙重驗證,還是其他流程。不要透過移除 guard,或授予一般使用者管理員角色,來解決驗證問題。
部分驗證攔截和節流機制僅在正式環境模式掛載,因此本機反覆嘗試成功,不代表線上不會觸發限制。持續重試也可能延長排查過程,應先停止重複操作,再核對目前設定。
三、註冊後為什麼要求電子郵件驗證
目前範本的 auth.emailVerification.defaultRequired 預設為 false,不要求所有新使用者先完成電子郵件驗證。使用者被要求驗證時,請檢查目前專案是否調整過設定,或是否正在執行一項個別要求驗證的帳號操作。
電子郵件驗證涉及驗證工作階段、郵件內容和目前的瀏覽器工作階段。收到多封郵件時,優先使用目前操作產生的最新郵件,不要混用另一次註冊、變更電子郵件地址或重設密碼的驗證碼。
如果提示無效或已過期,應檢查對應驗證工作階段和時間,不要手動將使用者的電子郵件驗證狀態改成成功。密碼重設還可能要求繼續完成雙重驗證,電子郵件驗證通過不代表整個重設流程已完成。
四、本機顯示寄送成功,卻沒有收到郵件
目前 npm run dev 使用開發模式,郵件元件會在終端機輸出:
[DEV EMAIL PREVIEW]其中包含收件地址、標題和內文,寄送結果使用開發預覽 ID。此時不會呼叫實際的郵件服務,即使 .env 已填入 Resend Key,也不能根據收件匣是否收到郵件,判斷開發流程是否成功。
請在本機終端機查看這次預覽,使用其中的驗證資訊完成流程。預覽內容可能包含驗證碼和驗證連結,不要直接上傳完整的終端機螢幕截圖。
npm run preview 會經過打包流程,不能將開發模式的郵件模擬行為套用到所有預覽或正式環境的執行方式。驗證實際投遞時,應使用設定完整的對應環境,並在服務供應商端確認投遞結果。
五、正式環境郵件寄送失敗
先執行 npm run doctor:remote,再檢查實際失敗的請求。診斷指令碼讀取的是本機 .env.production,不能證明線上 Worker 已載入這份最新設定。
目前範本預設使用 Resend:
| 專案位置 | 檢查內容 |
|---|---|
config/deploy.ts | emailProvider.type 是否為 resend |
.env.production | 是否已填入 RESEND_API_KEY,且屬於目標帳戶 |
config/base.ts | 實際寄件地址 fromEmailAddress.email,以及回覆地址 supportEmail |
| Resend 主控台 | 寄件網域是否已驗證、Key 是否有寄送權限、投遞是否失敗 |
| 線上 Worker | 修改 Secret 後,是否已執行更新部署 |
若改用 Cloudflare Email,請檢查 EMAIL 是否為 send_email 類型的 Binding。如果設定了 allowed_sender_addresses,其中必須包含實際寄件地址。也要依目前郵件服務的設定檢查收件限制,不能假設新增 Binding 後就能寄送至任意地址。
如果只有某類郵件失敗,請檢查 src/libs/email/template/factory.ts 中的範本註冊、呼叫資料和語言文案。設定錯誤、範本內容產生錯誤、服務供應商拒絕,以及最終退信,屬於不同階段。
服務供應商接受請求後仍未收到郵件,應繼續查看投遞、退信和垃圾郵件情形。應用程式傳回寄送成功,只表示這次寄送呼叫成功,不保證郵件已進入收件匣。
六、出現 emailSendTooOften
設定位於 deploy.spam.resourceProtection.emailSendService,預設時間範圍為 1d,上限為 5。雖然欄位名稱是 maxEmailsPerUser,目前受保護的寄送流程,實際上是以收件電子郵件地址產生計數 Key。
這是配額計算的時間範圍,不代表一定會在收件人所在時區的午夜重設。開發模式遇到配額問題時會記錄提示,正式環境模式則可能拒絕繼續寄送。
排查時,請確認是否有重複點擊、前端自動重試,或多個流程反覆寄信至同一地址的情形。先停止重複呼叫,再依業務需求評估設定,不要為了通過測試而清空線上計數,或無限制提高上限。
七、雙重驗證碼或復原碼無效
先確認帳號是否已完成雙重驗證設定,再檢查驗證器裝置的時間、目前使用的帳號項目,以及是否誤用舊設定中的驗證碼。
復原碼通常依設計只能使用一次。使用者提交已使用過的復原碼時,應採用剩餘的復原方式,不應修改資料庫,將它重新標記為未使用。
如果多位使用者同時出現雙重驗證資料無法解密的問題,請優先檢查發布時是否更換了 SAAS_SECRET。它也保護 OAuth 用戶端 Secret 和其他加密資料,應恢復專案原有設定並評估影響,而非再產生一把新金鑰。
八、已設定管理員電子郵件地址,卻仍無法進入後台
config/base.ts 的 adminEmails 並不是「每次登入自動成為管理員」的開關。
目前 UserEmailVerifiedEvent 僅在本站帳號的電子郵件驗證流程中,依已驗證地址與名單的精確比對結果,產生授予管理員角色的命令。密碼重設的電子郵件驗證不會授予角色,第三方 OAuth 傳回的電子郵件已驗證狀態,也不能取代這個流程。
請依序檢查:名單是否與已驗證地址一致、是否完成本站帳號驗證,以及相關 Event 和 grant_role Command 是否成功。已授予的角色也不會因為從設定名單刪除地址,就自動撤銷。撤銷必須透過對應的權限管理流程。
九、Google 或 GitHub 登入失敗
第三方登入與應用程式自行提供的 OAuth 服務,必須分別設定。Google、GitHub 登入使用以下回呼網址:
| 供應商 | 回呼網址 |
|---|---|
{VITE_SITE_URL}/api/auth/oauth2/google | |
| GitHub | {VITE_SITE_URL}/api/auth/oauth2/github |
回呼不符時,直接比較瀏覽器實際送出的 redirect_uri 與供應商後台的值,包含通訊協定、主機、連接埠和路徑。不要只檢查 .env 中「看起來正確」的網址。
按鈕未顯示或初始化失敗時,請檢查對應的 Client ID 和 Client Secret 是否成對填入。Google One Tap 還要求啟用 auth.enableGoogleOneTap,範本預設關閉這項功能。
回呼後狀態驗證失敗時,請檢查發起和完成登入是否使用同一個瀏覽器工作階段、同一個網站網址,以及是否使用了舊分頁中的授權連結。切換網域後,要同時更新平台回呼和應用程式設定。
各平台的設定步驟請見 Google OAuth 和 GitHub OAuth。
十、內建 OAuth 2.0 服務無法授權
內建服務使用 /oauth/*,其用戶端由本應用程式管理,不能使用 Google 或 GitHub 的 Client Secret 來請求本應用程式的權杖。
請檢查用戶端狀態、Redirect URI、允許的 Grant Type、請求的 Scope,以及 PKCE 參數。範本預設要求 S256,授權請求中的 Challenge,與交換權杖時的 Verifier,必須來自同一次授權流程。
授權碼不能當作長期驗證資訊重複使用。排查權杖交換失敗時,請重新開始一次完整授權,保留已遮蔽敏感資訊的協定錯誤名稱,不要記錄授權碼、用戶端 Secret 或權杖原始值。
若授權頁面提示升級,請檢查 config/upgrade.ts。目前範本的預設設定為空,自訂規則後,才會依角色或權益條件顯示對應的升級方案。規則比對與業務 API 的權限檢查,仍需分別驗證。
修正後的驗證
使用一般帳號完成一次登入、登出和再次登入。若涉及電子郵件,確認驗證狀態確實更新。若涉及 OAuth,從重新發起授權到回到應用程式,完整執行一次。若涉及管理員角色,再使用一般帳號確認仍無法存取後台。