Clerk 身份驗證模式
快速指南: Clerk 提供託管式身份驗證,包含預建的 UI 元件、伺服器端輔助函式和基於組織的多租戶支援。使用
clerkMiddleware()保護路由,使用<Show>進行條件式渲染,使用鉤子處理客戶端狀態,以及使用auth()/currentUser()進行伺服器端身份驗證。Clerk Core 3(2026)以<Show>取代<SignedIn>/<SignedOut>,將中介軟體檔案重新命名為proxy.ts(Next.js 16+),並合併了套件。
<critical_requirements>
重要事項:使用此技能前
所有程式碼必須遵循
CLAUDE.md中的專案慣例(kebab-case、具名匯出、匯入排序、import type、具名常數)
(所有伺服器端匯入必須使用 @clerk/nextjs/server——切勿從 @clerk/nextjs 匯入伺服器輔助函式)
(您必須使用 Clerk 的 verifyWebhook 輔助函式驗證 Webhook——切勿信任未經驗證的 Webhook 負載)
(您必須使用 <Show> 元件取代已棄用的 <SignedIn> / <SignedOut> / <Protect>——這些在 Core 3 中已被移除)
(切勿將完整的 currentUser() 物件傳遞給客戶端——它包含必須保留在伺服器端的 privateMetadata)
(您必須同時在中介軟體和資料存取層保護路由——僅靠中介軟體是不夠的)
</critical_requirements>
自動偵測: Clerk, ClerkProvider, clerkMiddleware, @clerk/nextjs, useUser, useAuth, useClerk, useSession, useOrganization, SignIn, SignUp, UserButton, UserProfile, OrganizationSwitcher, auth(), currentUser(), CLERK_SECRET_KEY, NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY, Show when="signed-in"
使用時機:
- 為應用程式新增身份驗證和用戶管理
- 使用基於組織的存取控制建構多租戶 B2B 應用程式
- 使用具有可自訂主題的預建登入/註冊 UI 元件
- 使用中介軟體和伺服器端授權檢查保護路由
- 透過 Webhook 將 Clerk 用戶資料同步到您的資料庫
涵蓋的主要模式:
- ClerkProvider 設定、環境變數、中介軟體配置
- 預建 UI 元件(
<SignIn>、<SignUp>、<UserButton>、<Show>) - 客戶端鉤子(
useUser、useAuth、useSession、useOrganization) - 伺服器端身份驗證(
auth()、currentUser())在 Server Components、Route Handlers、Server Actions 中 - 使用
clerkMiddleware()和createRouteMatcher()進行中介軟體路由保護 - 基於角色的組織多租戶和權限管理
- 使用 Svix 簽章驗證進行 Webhook 處理
何時不應使用:
- 需要自託管身份驗證(需要完全控制身份驗證資料儲存)
- 無法使用第三方身份驗證服務(合規/監管限制)
- 僅需要簡單的 API 金鑰身份驗證(自訂中介軟體就足夠了)
- 預算限制導致無法使用託管服務
詳細資源:
- reference.md - 決策框架、鉤子快速參考、Core 3 遷移備忘錄
- examples/core.md - ClerkProvider、環境變數、中介軟體配置
- examples/components.md - 預建元件、自訂、外觀屬性
- examples/hooks.md - useUser、useAuth、useSession、載入狀態、條件式渲染
- examples/server.md - Server Components、API 路由、Server Actions、Webhook 處理
- examples/organizations.md - 組織管理、角色、權限、RBAC
理念
Clerk 是一個託管式身份驗證平台,處理整個身份驗證生命週期:註冊、登入、會話管理、用戶資料、組織和 MFA。您不需要從頭開始建構身份驗證,而是整合 Clerk 的 SDK 和預建元件。
核心原則:
- 縱深防禦 - 在中介軟體層保護路由,並在每個資料存取點驗證身份驗證。僅靠中介軟體是不夠的(CVE-2025-29927 證明了中介軟體繞過漏洞)。
- 伺服器優先的身份驗證 - 在 Server Components 和 Route Handlers 中使用
auth()和currentUser()。僅在需要反應式客戶端狀態時才使用客戶端鉤子(useUser、useAuth)。 - 預建優於自訂 - 使用 Clerk 的
<SignIn>、<SignUp>、<UserButton>元件。僅在預建元件確實無法滿足需求時才建構自訂流程。 - 組織用於多租戶 - 對於 B2B 應用程式,使用具有角色和權限的 Clerk Organizations。不要在 Clerk 的用戶模型之上建構自訂租戶系統。
- Webhook 驅動的同步 - 透過 Webhook(而非輪詢)將 Clerk 資料同步到您的資料庫。始終使用
verifyWebhook驗證 Webhook 簽章。
何時使用 Clerk:
- 您需要快速實現身份驗證,且自訂程式碼最少
- 您需要登入/註冊/用戶管理的預建 UI 元件
- 您需要具有 RBAC 的基於組織的多租戶功能
- 您需要託管的 MFA、SSO(SAML/OIDC)和社交登入
何時不應使用 Clerk:
- 您需要完全控制身份驗證資料儲存(自託管要求)
- 您無法使用第三方身份驗證服務(合規/監管)
- 您的應用程式僅需要簡單的 API 金鑰身份驗證
- 預算限制導致無法使用託管服務
核心模式
模式 1:ClerkProvider 和中介軟體設定
每個 Clerk 應用程式都需要 <ClerkProvider> 包裝應用程式,以及 clerkMiddleware() 保護路由。完整的設定範例請參閱 examples/core.md。
關鍵規則:
ClerkProvider位於<body>內部,而不是包裝<html>——Core 3 要求這樣做- 使用
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY環境變數,切勿硬編碼金鑰 - 在 Next.js 16+ 上中介軟體檔案為
proxy.ts,在 Next.js <=15 上則為middleware.ts - Webhook 端點必須在公開路由清單中(由
verifyWebhook單獨驗證) CLERK_WEBHOOK_SIGNING_SECRET是用於 Webhook 簽章的環境變數(不是CLERK_WEBHOOK_SECRET)
// proxy.ts (Next.js 16+) 或 middleware.ts (Next.js <=15)
const isPublicRoute = createRouteMatcher([
"/",
"/sign-in(.*)",
"/sign-up(.*)",
"/api/webhooks(.*)",
]);
export default clerkMiddleware(async (auth, req) => {
if (!isPublicRoute(req)) {
await auth.protect();
}
});
為什麼這樣做是好的: 明確白名單公開路由,其他所有內容都需要身份驗證,Webhook 端點是公開的(由 signature 單獨驗證)
// 不好:使用無回呼的 clerkMiddleware()
export default clerkMiddleware(); // 所有路由預設都是公開的!
為什麼不好: 沒有回呼的 clerkMiddleware() 會附加身份驗證資料,但不會保護任何路由
模式 2:預建 UI 元件和 <Show>
使用 <Show> 進行條件式渲染(Core 3 中取代 <SignedIn> / <SignedOut> / <Protect>)。完整的範例請參閱 examples/components.md。
<Show when="signed-out"><SignInButton /></Show>
<Show when="signed-in"><UserButton /></Show>
<Show when={{ role: "org:admin" }}><AdminPanel /></Show>
<Show when={(has) => has({ permission: "org:invoices:manage" })}><InvoiceManager /></Show>
為什麼這樣做是好的: <Show> 是 Core 3 API,支援字串/物件/回呼條件,以及未經授權時的 fallback 屬性
// 不好:已棄用——在 Core 3 中已移除
<SignedIn>...</SignedIn>
<SignedOut>...</SignedOut>
<Protect role="admin">...</Protect>
為什麼不好: <SignedIn>、<SignedOut>、<Protect> 在 Core 3 中已移除,請使用帶有 when 屬性的 <Show>
登入/註冊頁面需要捕獲所有路由區段 [[...sign-in]],以支援多步驟流程(MFA、OAuth 回呼)。
模式 3:客戶端鉤子
在 Client Components 中使用鉤子來處理反應式身份驗證狀態。在存取資料前務必檢查 isLoaded。 完整的範例請參閱 examples/hooks.md。
"use client";
const { isLoaded, isSignedIn, user } = useUser();
if (!isLoaded) return <div>Loading...</div>;
if (!isSignedIn) return <div>Please sign in</div>;
// 現在可以安全地存取 user.firstName、user.emailAddresses 等
為什麼這樣做是好的: 防止在初始化期間存取 undefined 導致的水合錯誤,並防止在登出時存取 null 導致的 TypeError
// 不好:在沒有防護的情況下存取 user
const { user } = useUser();
return <p>{user.firstName}</p>; // 當 isLoaded=false 或 isSignedIn=false 時會發生執行時期錯誤
為什麼不好: 在載入完成前 user 是 undefined,在登出時是 null
關鍵鉤子:
useUser()——用戶個人資料資料(姓名、電子郵件、頭像)useAuth()——會話令牌(getToken())、userId、orgId、用於授權的has()useOrganization()——活躍組織資料、成員資格、分頁成員清單useReverification()——在敏感操作前重新驗證(Core 3)
在 Core 3 中,getToken() 在離線時會擲出 ClerkOfflineError 而不是回傳 null——務必包裝在 try/catch 中。
模式 4:伺服器端身份驗證
使用 @clerk/nextjs/server 中的 auth() 和 currentUser()。完整的範例請參閱 examples/server.md。
auth()——輕量級,無 API 呼叫,讀取會話宣告:
import { auth } from "@clerk/nextjs/server";
const { userId, orgId, isAuthenticated, redirectToSignIn } = await auth();
if (!isAuthenticated) return redirectToSignIn();
currentUser()——完整的用戶物件,會進行後端 API 呼叫:
import { currentUser } from "@clerk/nextjs/server";
const user = await currentUser();
// 重要:在傳遞給客戶端元件之前,挑選安全欄位
const safeData = { firstName: user.firstName, imageUrl: user.imageUrl };
為什麼重要: currentUser() 會回傳 privateMetadata,它絕對不能到達客戶端。請明確挑選欄位。
縱深防禦: 身份驗證必須在每個 Server Action 和 Route Handler 中檢查,而不僅僅是在中介軟體中:
"use server";
const { userId } = await auth();
if (!userId) throw new Error("Unauthorized");
// userId 將所有查詢/變更範圍限定給當前用戶
模式 5:使用角色和權限進行授權
使用 auth.protect() 或 has() 進行細粒度的存取控制。RBAC 模式請參閱 examples/organizations.md。
// 中介軟體:基於角色的路由保護
await auth.protect((has) => has({ role: "org:admin" }));
// Server Component:權限檢查
const { has } = await auth();
if (!has({ permission: "org:invoices:delete" })) redirect("/dashboard");
// 客戶端:按角色進行條件式渲染
<Show when={{ role: "org:admin" }}><AdminPanel /></Show>
權限格式: org:<feature>:<action>(例如 org:invoices:create、org:reports:read)
預設角色: org:admin(所有權限)、org:member(唯讀)。自訂角色在 Clerk Dashboard 中配置。
模式 6:Webhook 處理
使用 Clerk Webhook(透過 Svix)將用戶資料同步到您的資料庫。完整的處理程式請參閱 examples/server.md。
import { verifyWebhook } from "@clerk/nextjs/webhooks";
export async function POST(req: Request) {
const evt = await verifyWebhook(req); // 讀取 CLERK_WEBHOOK_SIGNING_SECRET 環境變數
// evt.type: "user.created" | "user.updated" | "user.deleted" | "organization.*" | ...
// evt.data: UserJSON | OrganizationJSON | 等等
}
為什麼這樣做是好的: verifyWebhook 驗證 Svix 簽章,始終回傳 200 以防止重試,Webhook 路由在中介軟體中是公開的(由簽章而非身份驗證驗證)
// 不好:無簽章驗證
const body = await req.json(); // 任何人都可以 POST 假資料!
await db.insert(users).values(body.data); // 安全漏洞
為什麼不好: 沒有簽章驗證意味著任何人都可以向您的端點發送假的 Webhook 事件
從 @clerk/shared/types 匯入型別(Core 3): UserJSON、OrganizationJSON、OrganizationMembershipJSON
<decision_framework>
決策框架
客戶端與伺服器端身份驗證
您在何處需要身份驗證資料?
|-- Server Component、Route Handler、Server Action
| |-- 僅需要 userId/sessionId?--> auth()
| |-- 需要完整的用戶物件?--> currentUser()
| |-- 需要保護路由?--> auth.protect()
| +-- 需要組織上下文?--> auth() 會回傳 orgId、orgRole
|
+-- Client Component(互動式 UI)
|-- 需要用戶個人資料資料?--> useUser()
|-- 需要會話/令牌資料?--> useAuth()
|-- 需要組織資料?--> useOrganization()
+-- 需要低階 Clerk API?--> useClerk()
路由保護策略
這是什麼類型的路由?
|-- 公開(登陸頁、登入、註冊、Webhooks)
| +-- 新增到 isPublicRoute 比對器,跳過 auth.protect()
|
|-- 需驗證(儀表板、個人資料、設定)
| +-- 在中介軟體中執行 auth.protect() + 在資料層執行 auth() 檢查
|
+-- 需授權(管理員、組織特定、權限控管)
|-- 基於角色?--> auth.protect({ role: "org:admin" })
+-- 基於權限?--> auth.protect((has) => has({ permission: "org:feature:action" }))
元件選擇
您需要什麼身份驗證 UI?
|-- 完整的登入頁面 --> 在捕獲所有路由上使用 <SignIn />
|-- 完整的註冊頁面 --> 在捕獲所有路由上使用 <SignUp />
|-- 登入按鈕(模態框)--> <SignInButton />
|-- 用戶頭像 + 選單 --> <UserButton />
|-- 完整的個人資料編輯器 --> <UserProfile />
|-- 組織切換器 --> <OrganizationSwitcher />
+-- 條件式內容 --> <Show when="signed-in"> 或 <Show when={{ role: "..." }}>
</decision_framework>
<red_flags>
危險信號
高優先級問題:
- 從
@clerk/nextjs而不是@clerk/nextjs/server匯入伺服器輔助函式(在 Server Components 中會中斷) - 使用已棄用的
<SignedIn>/<SignedOut>/<Protect>元件(在 Core 3 中已移除) - 將完整的
currentUser()物件傳遞給客戶端元件(洩漏privateMetadata) - 在沒有
verifyWebhook簽章驗證的情況下信任 Webhook 負載 - 僅依賴中介軟體進行路由保護(中介軟體可能被繞過)
- 從
@clerk/types而不是@clerk/shared/types匯入型別(Core 3 重新命名) - 假設
getToken()永遠不會擲出(在 Core 3 中離線時會擲出ClerkOfflineError) - 在客戶端元件中使用
currentUser()(這是一個非同步的伺服器端函式) - 在未先檢查
isLoaded的情況下存取useUser()的資料(導致水合錯誤) - 將 ClerkProvider 放在
<html>標籤外部(Core 3 需要放在<body>內部)
</red_flags>