使用者身分驗證與帳號
註冊、登入、電子郵件驗證、社群登入、2FA 與帳號中心。依產品需求完成設定後即可使用,不必重新實作身分驗證系統。
Saavo 範本內建完整的帳號系統,涵蓋註冊、登入、電子郵件驗證、忘記密碼、社群登入、雙重驗證、帳號中心與管理員身分等常見功能。
開發自己的產品時,通常不必重新實作這些功能。只要依產品需求調整身分驗證政策,設定好電子郵件、OAuth 等所需服務,再於頁面、API 與前端元件中串接並驗證使用者身分即可。
既有功能
範本初始化後,即可使用下列身分驗證功能:
| 功能 | 預設入口 | 預設狀態 |
|---|---|---|
| 註冊 | /auth/signup | 已啟用 |
| 登入 | /auth/login | 已啟用 |
| 忘記密碼 | /auth/forgot-password | 已啟用 |
| 電子郵件驗證 | /auth/verify-email | 註冊後寄送驗證郵件,但預設不強制驗證 |
| Google 登入 | 登入頁面 | 設定金鑰後啟用 |
| GitHub 登入 | 登入頁面 | 設定金鑰後啟用 |
| 雙重驗證 | /account | 使用者可自行啟用 |
| 帳號中心 | /account | 已登入的使用者可存取 |
| 管理後台 | /dashboard | 管理員可存取 |
除了上述功能,Saavo 也提供工作階段(Session)管理、登入彈出視窗、復原碼、敏感操作的再次驗證,以及其他與身分驗證相關的安全限制。
以 Saavo 範本開發自己的產品時,不必另外建立一套登入狀態,也不需要在業務程式碼中自行解析 Cookie 或直接操作 Session 資料。請以 Saavo 提供的身分驗證結果為基礎,開發自己的功能。
先體驗註冊與登入
修改身分驗證設定前,建議先使用預設設定,完整操作一次驗證流程,了解範本已提供的功能。
啟動開發伺服器後,開啟首頁,右上角會顯示登入按鈕:

按下登入按鈕,即可在彈出視窗中完成登入。Saavo 也提供獨立的登入頁面:

如果還沒有帳號,可以前往註冊頁面,填寫電子郵件地址與密碼完成註冊:

預設情況下,Saavo 會寄送電子郵件驗證信,但不強制使用者完成驗證,尚未驗證也能繼續使用產品。
本機開發時,範本不會實際寄送郵件,而是將模擬郵件輸出到開發伺服器的主控台。你可以直接從記錄中找到驗證碼,例如:
[DEV EMAIL PREVIEW] {
recipients: [ 'saavo@live.com' ],
subject: 'Welcome to Saavo Starter',
text: 'Hello, saavo,\n' +
'\n' +
'Thank you for registering with Saavo Starter! To ensure the security of your account, we need to verify your email address.\n' +
'\n' +
'Your verification code is 2RBFH7IA\n' +
'\n' +
'If you did not sign up for a Saavo Starter account, please disregard this email.\n' +
'\n' +
'If you have any questions, feel free to contact our support team.\n' +
'\n' +
'Best regards,\n' +
'The Saavo Starter Team',
html: ''
}使用上述郵件中的驗證碼,即可完成電子郵件驗證。如果沒有啟用強制驗證,也可以先略過:

登入後,透過右上角的頭像進入帳號中心:

帳號中心(/account)已包含個人資料、安全性設定(密碼、電子郵件、雙重驗證)、已購產品與付款紀錄等功能。
除了上述功能,範本也為管理員提供獨立的主控台頁面:

管理員的電子郵件地址設定於 adminEmails。只有地址符合設定,且已完成電子郵件驗證的使用者,才能取得管理員身分並存取 /dashboard。
提示
開發人員通常不必重新實作這些頁面,只要依產品需求調整身分驗證政策,讓業務功能沿用既有的驗證狀態即可。
設定使用者身分驗證
預設設定的目的是讓你快速啟動並體驗範本。正式開發產品時,建議至少確認以下幾個關鍵選項。
是否強制驗證電子郵件
預設情況下,使用者註冊後會收到驗證郵件,即使尚未完成驗證,也可以登入。
產品正式對外提供服務後,尤其是資源消耗較高、需要防範大量註冊,或需要確認帳號真實性的情境,建議啟用強制電子郵件驗證:
emailVerification: {
defaultRequired: true,
}啟用後,尚未完成電子郵件驗證的使用者存取受保護頁面時,會被引導至電子郵件驗證流程。
註冊後是否寄送驗證郵件,由另一個設定決定:
emailVerification: {
sendRegisterEmail: true,
}也就是說,「是否寄送驗證郵件」與「是否必須完成電子郵件驗證」是兩個獨立的設定。
電子郵件驗證預設採用驗證碼:
emailVerification: {
sendVerifyType: 'code',
}如果想讓使用者直接按下郵件中的連結完成驗證,請改為:
emailVerification: {
sendVerifyType: 'link',
}注意事項
啟用強制電子郵件驗證前,請先確認正式環境的郵件服務能正常運作,否則新註冊的使用者將無法通過驗證,繼續使用產品。
選擇登入方式
電子郵件與密碼登入預設已啟用。
此外,Saavo 也支援 Google 與 GitHub 登入。第三方登入會依目前的環境變數啟用,未設定對應的 OAuth 金鑰時,不會顯示該登入入口。
如果你的 SaaS 產品主要面向一般消費者,啟用 Google 登入通常能降低註冊門檻。如果產品面向開發人員,可以再依目標使用者決定是否提供 GitHub 登入。若需要其他第三方登入方式,例如 Apple 登入,則需參考相關文件自行實作。
使用 Google 登入時,也可以透過以下設定啟用 Google One Tap:
enableGoogleOneTap: trueClient ID、Client Secret 與 Callback URL 的設定方式,請參考後文「準備身分驗證服務」。
是否強制使用雙重驗證
Saavo 已提供以驗證器 App 為基礎的雙重驗證與復原碼。
預設設定如下:
twoFactorAuth: {
defaultRequired: false,
}2FA 是選用功能。上述選項表示所有使用者預設不啟用,但使用者隨時都能在 /account 的安全性設定中自行啟用。

這種方式適合大多數 SaaS 產品。需要額外安全保護的使用者可以自行啟用,其他使用者也不必增加註冊與使用上的步驟。
如果產品有更高的安全需求,可以設定為:
twoFactorAuth: {
defaultRequired: true,
}啟用後,使用者必須先完成雙重驗證設定,才能使用產品。
復原碼的數量可以透過以下設定調整:
twoFactorAuth: {
recoveryCodeCount: 5,
}另外,初始化產品時,記得修改 twoFactorAuth.issuer:
twoFactorAuth: {
issuer: 'Acme',
}issuer 會顯示在 Google Authenticator、1Password 等驗證器 App 中,請改成自己的產品名稱。
設定登入後的重新導向頁面
範本預設在註冊、登入與登出後返回首頁。
實際的 SaaS 產品通常會讓使用者登入後直接進入應用程式。例如,產品的主要介面位於 /app:
ui: {
redirectTo: {
afterSignup: '/app',
afterLogin: '/app',
afterLogout: '/',
},
}調整後的流程如下:
註冊成功 → /app
登入成功 → /app
登出 → /建議及早調整這部分設定,避免使用者登入後仍被帶回範本的預設首頁。
選擇登入彈出視窗或登入頁面
Saavo 預設啟用登入彈出視窗。使用者可以按下右上角的登入按鈕開啟視窗,也可能在操作業務功能時,由系統提示登入。
例如,尚未登入的使用者按下購買按鈕時,可以在目前頁面完成登入,接著繼續原本的付款流程,不必先前往登入頁面。
如果不需要登入彈出視窗,可以關閉 ui.useLoginModal:
ui: {
useLoginModal: false,
}關閉後,需要登入時,系統會統一重新導向完整的登入頁面。
兩種方式各有適合的情境。如果使用者經常在操作途中被要求登入,彈出視窗較自然。如果登入本身就是明確的獨立步驟,使用完整的登入頁面會比較單純。
新增管理員
Saavo 透過 config/base.ts 中的 adminEmails 設定管理員:
adminEmails: ['admin@saavo.dev'],使用該電子郵件地址註冊帳號並完成驗證後,即可取得管理員身分並存取:
/dashboardadminEmails 不會建立可略過身分驗證的超級帳號。管理員首先是一般使用者,只有已完成驗證的電子郵件地址與設定中的地址完全相符,才會取得管理員身分。
建議在產品初始化後,及早建立自己的管理員帳號,並確認能正常進入管理後台。
準備身分驗證服務
範本已內建身分驗證邏輯,但電子郵件、OAuth、Turnstile 等功能仍需要外部服務與環境變數。
SAAS_SECRET
每個專案都需要設定 SAAS_SECRET 環境變數,請避免在不同專案中重複使用相同的值:
SAAS_SECRET=...Session 與其他敏感資料的加密都會用到這個值,建議每個專案獨立設定。
電子郵件服務
下列身分驗證流程都需要寄送郵件:
- 註冊後的電子郵件驗證
- 重新寄送驗證碼
- 忘記密碼
- 修改電子郵件地址
- 其他需要透過電子郵件確認的安全操作
本機開發時,Saavo 會直接使用主控台輸出的模擬郵件,不會實際寄送。
正式部署後,需要設定實際寄送郵件的服務。支援的郵件服務與設定方式,請參考:
如果啟用了強制電子郵件驗證,應先確認郵件服務能正常寄送郵件。
Google 與 GitHub 登入
如果需要 Google 登入,請在 .env 中設定:
GOOGLE_CLIENT_ID=...
GOOGLE_CLIENT_SECRET=...Google OAuth 應用程式中的 Redirect URI 應設為:
https://your-domain.com/api/auth/oauth2/google如果需要 GitHub 登入:
GITHUB_CLIENT_ID=...
GITHUB_CLIENT_SECRET=...對應的 Redirect URI 為:
https://your-domain.com/api/auth/oauth2/github未設定對應的金鑰時,不會顯示該登入方式。
修改 .env 後,需要重新啟動開發伺服器。
完整的 Google 登入設定流程,請參考:
Turnstile 人機驗證
Saavo 支援使用 Cloudflare Turnstile,為註冊、登入等身分驗證頁面加入人機驗證。
預設不會每次開啟頁面都顯示 Turnstile,而是依身分驗證操作的風險計數觸發:
useTurnstile: {
threshold: 2,
interval: '1h',
}其中:
threshold是觸發人機驗證的風險計數門檻。interval是風險計數的有效期間。
依預設設定,對應操作的風險計數達到 2 時,就會要求人機驗證。參數錯誤、身分驗證失敗與部分檢查事件會增加計數,成功事件則可以降低計數,因此不能將它理解為「重新整理頁面超過兩次」。

如果希望更早觸發驗證,可以調低門檻。不需要 Turnstile 時,也可以直接設定:
useTurnstile: false啟用 Turnstile 時,需要設定:
CLOUDFLARE_TURNSTILE_SITE_KEY=...
CLOUDFLARE_TURNSTILE_SECRET_KEY=...本機開發可以使用 Cloudflare 提供的測試金鑰,正式環境則應使用為自己網站申請的正式金鑰。
在業務功能中驗證使用者身分
完成設定後,下一步是讓業務程式碼讀取目前使用者的身分驗證狀態。
基本原則是:
不要在業務程式碼中自行讀取 Cookie,也不要直接查詢或修改 Session 資料表。
Saavo 已透過 Guard 統一處理登入狀態、電子郵件驗證、雙重驗證等條件,業務程式碼直接使用驗證結果即可。
保護頁面
許多 SaaS 頁面只允許已登入的使用者存取,例如帳號中心、工作台,或使用者自己的專案頁面。
由伺服器端呈現的頁面,可以在頁面的 Handler 中呼叫 authenticatedGuard:
const accountPageHandlers = createLayoutHandlers(
createLayoutRenderer('page'),
(c: Ctx) => {
const locale = c.var.getLocale();
const guardResult = authenticatedGuard(c);
if (!guardResult.success) {
return c.redirect(
getFullPath(guardResult.details?.redirectUrl as string, locale),
);
}
const { authContext } = guardResult.data;
return <AccountPage c={c} />;
},
);
export default accountPageHandlers;authenticatedGuard 不只判斷使用者是否有 Session。
它會依目前的身分驗證政策,自動處理不同的驗證狀態:
尚未登入
↓
登入頁面
需要驗證電子郵件,但尚未驗證
↓
電子郵件驗證
要求 2FA,但尚未完成
↓
雙重驗證流程
已滿足所有身分驗證要求
↓
繼續執行業務頁面因此,業務頁面不必重複實作登入、電子郵件驗證與 2FA 的判斷。
如果頁面還需要管理員權限、角色或 Capability,在通過身分驗證後,再執行對應的檢查即可。
完整範例請參考:
保護 API
API 使用同一套身分驗證 Guard,只是在失敗時回傳 JSON,而不是重新導向:
const guardResult = authenticatedGuard(c);
if (!guardResult.success) {
return c.json(
guardResult,
guardResult.code === gResultCode.authLoginRequired ? 401 : 403,
);
}
const { authContext } = guardResult.data;這樣就能讓頁面與 API 的身分驗證規則保持一致。
日後將電子郵件驗證或 2FA 從選用改為強制時,也不必逐一修改業務 API 的驗證邏輯。
使用 authContext 處理使用者的業務操作
驗證成功後,可以從 authContext 物件取得目前請求的身分驗證資訊,其中包含目前使用者與 Session 的資訊。後續業務邏輯會透過它取得使用者身分,也以此作為權限控制的基礎。
例如,使用者正在建立自己的專案:
POST /api/projects伺服器端需要知道這個專案屬於誰。
此時應使用身分驗證執行脈絡中的目前使用者身分,而不是直接信任前端送出的 userId。
類似的業務操作還包括:
- 建立屬於目前使用者的資料
- 查詢目前使用者的專案
- 修改自己的資源
- 判斷目前的角色
- 檢查 Entitlement
- 檢查 Capability
- 判斷目前使用者是否可以執行某項操作
整體流程通常如下:
請求
↓
authenticatedGuard()
↓
authContext
↓
角色 / 使用權益 / 能力
↓
業務邏輯身分驗證負責確認「目前是誰」,Role、Entitlement 與 Capability 則進一步決定「目前使用者可以做什麼」。
在前端讀取目前使用者
除了伺服器端的身分驗證,前端元件有時也需要知道目前使用者是否已登入。
Saavo 採用 MPA 頁面呈現方式,跨元件共用的前端狀態需要透過 Store 管理。目前的使用者狀態以 nanostores 實作,你可以透過 useCurrentUser 取得:
import { setLoginModalOpen } from '@/stores/login-modal';
import { useCurrentUser } from '@/stores/user';
const { isLoggedIn } = useCurrentUser();
if (!isLoggedIn) {
setLoginModalOpen(true);
return;
}這個範例會在使用者尚未登入時,開啟登入彈出視窗。
其他元件修改使用者資料後,也可以透過 setCurrentUser 更新共用狀態。
請注意,前端的 isLoggedIn 主要用來控制介面行為,例如顯示登入按鈕、開啟登入彈出視窗,或隱藏某些入口。
資料存取與權限控制仍必須在伺服器端透過 Guard、Role、Entitlement 或 Capability 檢查,不能以前端狀態作為安全邊界。
擴充帳號設定
/account 已涵蓋大多數帳號層級的設定,不必重複實作。
適合繼續放在 /account 的內容包括:
- 使用者資料
- 電子郵件地址
- 密碼
- 雙重驗證
- 復原碼
- 已購產品
- 付款與帳單資訊
這些功能都與使用者帳號本身有關。
屬於特定 SaaS 業務的設定,則應放在該業務的頁面中。例如,AI 產品可能還需要:
預設 AI 模型
生成參數
工作區設定
通知設定
團隊設定這些內容應由業務模組自行實作,不要全部加入 /account。
可以用一個簡單的方式判斷:
與「使用者是誰」有關的設定放在
/account,與「產品如何運作」有關的設定放在業務頁面。
上線檢查
身分驗證是產品上線前必須完整驗證的基礎功能,建議至少檢查下列流程:
- 註冊、電子郵件驗證、登入與登出流程正常。
- 可以順利完成忘記密碼流程。
-
/account中的帳號功能可以正常使用。 - 已啟用的 Google / GitHub 第三方登入可以順利完成。
- 若已啟用 2FA,可以完成啟用、驗證與復原碼流程。
- 尚未登入的使用者無法存取受保護的頁面與 API。
- 一般使用者無法存取
/dashboard。 - 管理員可以正常存取
/dashboard。 - 正式環境使用專案專屬的
SAAS_SECRET。 - 正式環境可以正常寄送郵件。
- OAuth 的 Redirect URI 已改為正式環境的網域。
- Turnstile 等身分驗證安全設定已使用正式環境的金鑰。
建議使用新註冊的一般帳號與管理員帳號,完整測試一次,不要只使用開發期間一直沿用的舊測試帳號。
常見問題
接下來
依接下來要開發的功能,可以繼續閱讀:
- 想啟用 Google 登入 → 串接 Google 登入
- 想建立登入後才能存取的頁面 → 登入後才能存取的頁面
- 想建立管理員頁面 → 管理員頁面
- 想設定身分驗證郵件 → 電子郵件
- 想限制使用者可以使用哪些功能 → 付費後才能使用的功能
- 想查詢身分驗證 API 與回傳格式 → API
大多數產品完成本章的設定後,就不需要再修改身分驗證系統本身。
接下來,只要以目前登入的使用者為基礎,串接產品實際需要的業務功能即可。