Communitygithub.com

agents-inc/skills

Clerk 管理的認證 - ClerkProvider、中介軟體、預建元件、鉤子、伺服器端認證、組織、Webhooks

skills 是什麼?

這項技能提供了使用 Clerk 進行身份驗證和用戶管理的完整模式,Clerk 是一個託管的身份驗證平台,處理註冊、登入、會話管理、用戶個人資料、組織和多重要素驗證 (MFA)。它涵蓋了從設置 `ClerkProvider` 和 `clerkMiddleware()` 以保護路由,到使用預建的 UI 元件(如 `<SignIn>`、`<SignUp>` 和 `<UserButton>`)以及使用 `<Show>` 元件進行條件式渲染的整個開發生命週期。開發人員將學習如何在 Server Components 和 Route Handlers 中使用伺服器端函數 `auth()` 和 `currentUser()`,以及如何在客戶端元件中使用 `useUser`、`useAuth` 和 `useOrganization` 等鉤子來實現反應式狀態管理。該技能還涵蓋了高級主題,例如使用組織進行多租戶和基於角色的存取控制 (RBAC),以及使用 `verifyWebhook` 簽章驗證來安全地將 Clerk 資料同步到您的資料庫。

相容平台✓Claude Code~Codex CLI~Cursor
npx skills add https://github.com/agents-inc/skills/tree/HEAD/src/skills/api-auth-clerk

在你喜歡的 AI 中提問

開啟一個已預先載入此 Agent Skill 的新對話。

說明文件

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 金鑰身份驗證(自訂中介軟體就足夠了)
  • 預算限制導致無法使用託管服務

詳細資源:


理念

Clerk 是一個託管式身份驗證平台,處理整個身份驗證生命週期:註冊、登入、會話管理、用戶資料、組織和 MFA。您不需要從頭開始建構身份驗證,而是整合 Clerk 的 SDK 和預建元件。

核心原則:

  1. 縱深防禦 - 在中介軟體層保護路由,並在每個資料存取點驗證身份驗證。僅靠中介軟體是不夠的(CVE-2025-29927 證明了中介軟體繞過漏洞)。
  2. 伺服器優先的身份驗證 - 在 Server Components 和 Route Handlers 中使用 auth() 和 currentUser()。僅在需要反應式客戶端狀態時才使用客戶端鉤子(useUser、useAuth)。
  3. 預建優於自訂 - 使用 Clerk 的 <SignIn>、<SignUp>、<UserButton> 元件。僅在預建元件確實無法滿足需求時才建構自訂流程。
  4. 組織用於多租戶 - 對於 B2B 應用程式,使用具有角色和權限的 Clerk Organizations。不要在 Clerk 的用戶模型之上建構自訂租戶系統。
  5. 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>

Individual skills in this repo

This repo contains 11 individual skills — each has its own dedicated page.

相關技能