关于 clerk-auth
clerk-auth 是 Clerk 身份验证的实现指南,面向使用 React、Next.js 或 Cloudflare Workers 的开发者。内容涵盖应用配置、路由保护、令牌验证、自定义 JWT 声明和测试。
主要功能
- 01配置 React 和 Next.js 身份验证
- 02保护应用路由
- 03在 Cloudflare Workers 验证令牌
- 04创建自定义 JWT 声明
- 05使用测试凭据进行验证
适用场景
- 01为 React 或 Next.js 应用加入登录
- 02限制未授权请求访问路由
- 03在 Cloudflare Workers 检查会话令牌
- 04为多租户应用设置 JWT 声明
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ jackspace/ claudeskillz --skill clerk-auth安装后在 Claude Code 中输入/clerk-auth确认已加载。
附带文件README.mdSKILL.json
详细介绍
Clerk 身份验证
状态:已可用于生产 ✅ 最后更新:2025-10-28 依赖项:无 最新版本:@clerk/[email protected]、@clerk/[email protected]、@clerk/[email protected]、@clerk/[email protected]
快速开始(10 分钟)
选择你的框架:
- React (Vite) - ClerkProvider + Hooks
- Next.js App Router - 中间件 + 异步 auth()
- Cloudflare Workers - 后端验证
React (Vite) 设置
1. 安装 Clerk
```bash npm install @clerk/clerk-react ```
最新版本:@clerk/[email protected](已于 2025-10-22 验证)
2. 配置 ClerkProvider
更新 `src/main.tsx`:
```typescript import React from 'react' import ReactDOM from 'react-dom/client' import { ClerkProvider } from '@clerk/clerk-react' import App from './App.tsx' import './index.css'
// 从环境变量获取可发布密钥 const PUBLISHABLE_KEY = import.meta.env.VITE_CLERK_PUBLISHABLE_KEY
if (!PUBLISHABLE_KEY) { throw new Error('Missing Publishable Key') }
ReactDOM.createRoot(document.getElementById('root')!).render( <React.StrictMode> </React.StrictMode>, ) ```
重要:
- 在 Vite 中,环境变量需使用 `VITE_` 前缀
- ClerkProvider 必须包裹整个应用
- 来源:https://clerk.com/docs/references/react/clerk-provider
3. 添加环境变量
创建 `.env.local`:
```bash VITE_CLERK_PUBLISHABLE_KEY=pk_test_... ```
安全提示:只有带有 `VITE_` 前缀的变量才会暴露给客户端代码。
4. 使用身份验证 Hooks
```typescript import { useUser, useAuth, useClerk } from '@clerk/clerk-react'
function App() { // 获取用户对象(包括电子邮件、元数据等) const { isLoaded, isSignedIn, user } = useUser()
// 获取身份验证状态和会话方法 const { userId, sessionId, getToken } = useAuth()
// 获取 Clerk 实例以执行高级操作 const { openSignIn, signOut } = useClerk()
// 渲染依赖身份验证的 UI 前,务必检查 isLoaded if (!isLoaded) { return
if (!isSignedIn) { return <button onClick={() => openSignIn()}>Sign In }
return (
Welcome {user.firstName}!
Email: {user.primaryEmailAddress?.emailAddress}
<button onClick={() => signOut()}>Sign Out重要原因:
- `isLoaded` 可避免错误内容闪现
- `useUser()` 提供包含元数据的完整用户对象
- `useAuth()` 提供会话令牌和身份验证状态
- 来源:https://clerk.com/docs/references/react/use-user
Next.js App Router 设置
1. 安装 Clerk
```bash npm install @clerk/nextjs ```
最新版本:@clerk/[email protected](已于 2025-10-22 验证)
- v6 新增:异步 auth() 助手、支持 Next.js 15、默认静态渲染
- 来源:https://clerk.com/changelog/2024-10-22-clerk-nextjs-v6
2. 配置环境变量
创建 `.env.local`:
```bash NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_test_... CLERK_SECRET_KEY=sk_test_...
可选:自定义登录/注册页面
NEXT_PUBLIC_CLERK_SIGN_IN_URL=/sign-in NEXT_PUBLIC_CLERK_SIGN_UP_URL=/sign-up NEXT_PUBLIC_CLERK_AFTER_SIGN_IN_URL=/dashboard NEXT_PUBLIC_CLERK_AFTER_SIGN_UP_URL=/onboarding ```
重要:
- 绝不可将 `CLERK_SECRET_KEY` 暴露给客户端
- 仅客户端变量使用 `NEXT_PUBLIC_` 前缀
- 来源:https://clerk.com/docs/guides/development/clerk-environment-variables
3. 添加路由保护中间件
在项目根目录创建 middleware.ts:
import { clerkMiddleware, createRouteMatcher } from '@clerk/nextjs/server'
// 定义公开路由(其他所有路由都需要身份验证)
const isPublicRoute = createRouteMatcher([
'/',
'/sign-in(.*)',
'/sign-up(.*)',
'/api/webhooks(.*)', // Clerk Webhook 应为公开路由
])
export default clerkMiddleware(async (auth, request) => {
// 保护所有非公开路由
if (!isPublicRoute(request)) {
await auth.protect()
}
})
export const config = {
matcher: [
// 跳过 Next.js 内部路由和静态文件
'/((?!_next|[^?]*\\.(?:html?|css|js(?!on)|jpe?g|webp|png|gif|svg|ttf|woff2?|ico|csv|docx?|xlsx?|zip|webmanifest)).*)',
// 始终对 API 路由运行
'/(api|trpc)(.*)',
],
}
重要:
- v6 中的
auth.protect()是异步的(相较于 v5 的破坏性变更) createRouteMatcher()接受 glob 模式- 替代方案:保护指定路由,而不是反转逻辑
- 来源:https://clerk.com/docs/reference/nextjs/clerk-middleware
4. 使用 ClerkProvider 包装应用
更新 app/layout.tsx:
import { ClerkProvider } from '@clerk/nextjs'
import './globals.css'
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<ClerkProvider>
<html lang="en">
<body>{children}</body>
</html>
</ClerkProvider>
)
}
5. 在服务器组件中使用 auth()
import { auth, currentUser } from '@clerk/nextjs/server'
export default async function DashboardPage() {
// 获取身份验证状态(轻量级)
const { userId, sessionId } = await auth()
// 获取完整用户对象(开销更大,调用次数更少)
const user = await currentUser()
if (!userId) {
return <div>未授权</div>
}
return (
<div>
<h1>控制面板</h1>
<p>用户 ID:{userId}</p>
<p>电子邮件:{user?.primaryEmailAddress?.emailAddress}</p>
</div>
)
}
重要:
- v6 中的
auth()是异步的(破坏性变更) - 轻量级检查使用
auth() - 需要完整用户对象时使用
currentUser()
Cloudflare Workers 设置
1. 安装依赖项
npm install @clerk/backend hono
最新版本:
- @clerk/[email protected](已于 2025-10-22 验证)
- [email protected]
2. 配置环境变量
为本地开发创建 .dev.vars:
CLERK_SECRET_KEY=sk_test_...
CLERK_PUBLISHABLE_KEY=pk_test_...
生产环境:使用 wrangler secret put CLERK_SECRET_KEY
3. 实现令牌验证
创建 src/index.ts:
import { Hono } from 'hono'
import { verifyToken } from '@clerk/backend'
type Bindings = {
CLERK_SECRET_KEY: string
CLERK_PUBLISHABLE_KEY: string
}
type Variables = {
userId: string | null
sessionClaims: any | null
}
const app = new Hono<{ Bindings: Bindings; Variables: Variables }>()
// 中间件:验证 Clerk 令牌
app.use('/api/*', async (c, next) => {
const authHeader = c.req.header('Authorization')
if (!authHeader) {
c.set('userId', null)
c.set('sessionClaims', null)
return next()
}
const token = authHeader.replace('Bearer ', '')
try {
const { data, error } = await verifyToken(token, {
secretKey: c.env.CLERK_SECRET_KEY,
// 重要:设置 authorizedParties 以防止 CSRF 攻击
authorizedParties: ['https://yourdomain.com'],
})
if (error) {
console.error('令牌验证失败:', error)
c.set('userId', null)
c.set('sessionClaims', null)
} else {
c.set('userId', data.sub)
c.set('sessionClaims', data)
}
} catch (err) {
console.error('令牌验证错误:', err)
c.set('userId', null)
c.set('sessionClaims', null)
}
return next()
})
// 受保护的路由
app.get('/api/protected', (c) => {
const userId = c.get('userId')
if (!userId) {
return c.json({ error: '未授权' }, 401)
}
return c.json({
message: '这是受保护的内容',
userId,
sessionClaims: c.get('sessionClaims'),
})
})
export default app
关键:
- 始终设置
authorizedParties以防止 CSRF 攻击 - 使用
secretKey,不要使用已弃用的apiKey - 来源:https://clerk.com/docs/reference/backend/verify-token
JWT 模板与自定义声明
Clerk 允许使用模板自定义 JWT(JSON Web Token)结构。这使其能够与第三方服务集成、实现基于角色的访问控制,并支持多租户应用。
快速入门:创建 JWT 模板
1. 前往 Clerk 控制台:
- 前往 Sessions 页面
- 点击 Customize session token
- 点击 Create template
2. 定义模板:
{
"user_id": "{{user.id}}",
"email": "{{user.primary_email_address}}",
"role": "{{user.public_metadata.role || 'user'}}"
}
3. 在代码中使用模板:
// Frontend (React/Next.js)
const { getToken } = useAuth()
const token = await getToken({ template: 'my-template' })
// Backend (Cloudflare Workers)
const sessionClaims = c.get('sessionClaims')
const role = sessionClaims?.role
可用的短代码
| 类别 | 短代码 | 示例 |
|---|---|---|
| 用户 ID 和姓名 | {{user.id}}、{{user.first_name}}、{{user.last_name}}、{{user.full_name}} | "John Doe" |
| 联系信息 | {{user.primary_email_address}}、{{user.primary_phone_address}} | "[email protected]" |
| 个人资料 | {{user.image_url}}、{{user.username}}、{{user.created_at}} | "https://..." |
| 验证状态 | {{user.email_verified}}、{{user.phone_number_verified}} | true |
| 元数据 | {{user.public_metadata}}、{{user.public_metadata.FIELD}} | {"role": "admin"} |
| 组织 | org_id、org_slug、org_role(位于 sessionClaims 中) | "org:admin" |
高级功能
字符串插值:
{
"full_name": "{{user.last_name}} {{user.first_name}}",
"greeting": "Hello, {{user.first_name}}!"
}
条件回退值:
{
"role": "{{user.public_metadata.role || 'user'}}",
"age": "{{user.public_metadata.age || 18}}",
"verified": "{{user.email_verified || user.phone_number_verified}}"
}
使用点表示法访问嵌套元数据:
{
"interests": "{{user.public_metadata.profile.interests}}",
"department": "{{user.public_metadata.department}}"
}
默认声明(自动包含)
每个 JWT 都会自动包含以下声明(无法覆盖):
{
"azp": "http://localhost:3000", // Authorized party
"exp": 1639398300, // Expiration time
"iat": 1639398272, // Issued at
"iss": "https://your-app.clerk.accounts.dev", // Issuer
"jti": "10db7f531a90cb2faea4", // JWT ID
"nbf": 1639398220, // Not before
"sub": "user_1deJLArSTiWiF1YdsEWysnhJLLY" // User ID
}
大小限制:自定义声明为 1.2KB
问题:浏览器 Cookie 的大小限制为 4KB。Clerk 的默认声明约占 2.8KB,因此自定义声明仅剩 1.2KB。
⚠️ 开发提示:在 Vite 开发模式下测试自定义声明时,可能会遇到 “431 Request Header Fields Too Large” 错误。这是因为 URL 中的 Clerk 握手令牌超过了 Vite 的 8KB 限制。解决方案请参阅 问题 #11。
解决方案:
// ✅ GOOD: Minimal claims
{
"user_id": "{{user.id}}",
"email": "{{user.primary_email_address}}",
"role": "{{user.public_metadata.role}}"
}
// ❌ BAD: Exceeds limit
{
"bio": "{{user.public_metadata.bio}}", // 6KB field
"all_metadata": "{{user.public_metadata}}" // Entire object
}
最佳实践:将较大的数据存储在数据库中,只在 JWT 中包含标识符和角色。
TypeScript 类型安全
添加全局类型声明以启用自动补全:
创建 types/globals.d.ts:
export {}
declare global {
interface CustomJwtSessionClaims {
metadata: {
role?: 'admin' | 'moderator' | 'user'
onboardingComplete?: boolean
organizationId?: string
}
}
}
常见用例
基于角色的访问控制:
{
"email": "{{user.primary_email_address}}",
"role": "{{user.public_metadata.role || 'user'}}",
"permissions": "{{user.public_metadata.permissions}}"
}
多租户应用:
{
"user_id": "{{user.id}}",
"org_id": "{{user.public_metadata.org_id}}",
"org_role": "{{user.public_metadata.org_role}}"
}
Supabase 集成:
{
"email": "{{user.primary_email_address}}",
"app_metadata": {
"provider": "clerk"
},
"user_metadata": {
"full_name": "{{user.full_name}}"
}
}
另请参阅
- 完整参考:请参阅
references/jwt-claims-guide.md,了解完整文档 - 模板示例:请参阅
templates/jwt/目录,查看可运行的示例 - TypeScript 类型:请参阅
templates/typescript/custom-jwt-types.d.ts - 官方文档:https://clerk.com/docs/guides/sessions/jwt-templates
测试
Clerk 为本地开发和 CI/CD 流水线提供全面的测试工具。
快速开始:测试凭据
测试邮箱(不会发送邮件,使用固定 OTP):
[email protected]
[email protected]
测试电话号码(不会发送短信,使用固定 OTP):
+12015550100
+19735550133
固定 OTP 代码:424242(适用于所有测试凭据)
生成会话令牌
测试 API 端点时,生成有效的会话令牌(有效期为 60 秒):
# Using the provided script
CLERK_SECRET_KEY=sk_test_... node scripts/generate-session-token.js
# Create new test user
CLERK_SECRET_KEY=sk_test_... node scripts/generate-session-token.js --create-user
# Auto-refresh token every 50 seconds
CLERK_SECRET_KEY=sk_test_... node scripts/generate-session-token.js --refresh
手动流程:
- 创建用户:
POST /v1/users - 创建会话:
POST /v1/sessions - 生成令牌:
POST /v1/sessions/{session_id}/tokens - 在标头中使用:
Authorization: Bearer <token>
使用 Playwright 进行 E2E 测试
安装 @clerk/testing,以自动管理 Testing Token:
npm install -D @clerk/testing
全局设置(global.setup.ts):
import { clerkSetup } from '@clerk/testing/playwright'
import { test as setup } from '@playwright/test'
setup('global setup', async ({}) => {
await clerkSetup()
})
测试文件(auth.spec.ts):
import { setupClerkTestingToken } from '@clerk/testing/playwright'
import { test } from '@playwright/test'
test('sign up', async ({ page }) => {
await setupClerkTestingToken({ page })
await page.goto('/sign-up')
await page.fill('input[name="emailAddress"]', '[email protected]')
await page.fill('input[name="password"]', 'TestPassword123!')
await page.click('button[type="submit"]')
// Verify with fixed OTP
await page.fill('input[name="code"]', '424242')
await page.click('button[type="submit"]')
await expect(page).toHaveURL('/dashboard')
})
测试令牌(绕过机器人检测)
Testing Token 可在测试套件中绕过机器人检测。
获取令牌:
curl -X POST https://api.clerk.com/v1/testing_tokens \
-H "Authorization: Bearer sk_test_..."
在前端 API 请求中使用:
POST https://your-app.clerk.accounts.dev/v1/client/sign_ups?__clerk_testing_token=TOKEN
注意:@clerk/testing 会在 Playwright/Cypress 中自动处理此事。
生产环境限制
Testing Token 在开发和生产环境中均可使用,但:
- ❌ 生产环境不支持基于代码的身份验证(SMS/Email OTP)
- ✅ 支持邮箱 + 密码身份验证
- ✅ 支持魔法链接
另请参阅
- 完整指南:请参阅
references/testing-guide.md,了解完整的测试文档 - 会话令牌脚本:请参阅
scripts/generate-session-token.js - 演示仓库:https://github.com/clerk/clerk-playwright-nextjs
- 官方文档:https://clerk.com/docs/guides/development/testing/overview
已知问题预防
此技能可预防 11 个已记录的问题:
问题 #1:缺少 Clerk Secret Key
错误:"Missing Clerk Secret Key or API Key" 来源:https://stackoverflow.com/questions/77620604 预防措施:始终在 `.env.local` 中设置,或通过 `wrangler secret put` 设置
问题 #2:从 API Key 迁移到 Secret Key
错误:"apiKey is deprecated, use secretKey" 来源:https://clerk.com/docs/upgrade-guides/core-2/backend 预防措施:在所有调用中将 `apiKey` 替换为 `secretKey`
问题 #3:JWKS 缓存竞争条件
错误:"No JWK available" 来源:https://github.com/clerk/javascript/blob/main/packages/backend/CHANGELOG.md 预防措施:使用 @clerk/[email protected] 或更高版本(已修复)
问题 #4:缺少 authorizedParties(CSRF)
错误:不会报错,但存在 CSRF 漏洞 来源:https://clerk.com/docs/reference/backend/verify-token 预防措施:始终设置 `authorizedParties: ['https://yourdomain.com']`
问题 #5:导入路径变更(Core 2)
错误:"Cannot find module" 来源:https://clerk.com/docs/upgrade-guides/core-2/backend 预防措施:更新 Core 2 的导入路径
问题 #6:超出 JWT 大小限制
错误:令牌超出大小限制 来源:https://clerk.com/docs/backend-requests/making/custom-session-token 预防措施:自定义声明保持在 1.2KB 以下
问题 #7:API 版本 v1 已弃用
错误:"API version v1 is deprecated" 来源:https://clerk.com/docs/upgrade-guides/core-2/backend 预防措施:使用最新的 SDK 版本(API v2025-04-10)
问题 #8:ClerkProvider JSX 组件错误
错误:"cannot be used as a JSX component" 来源:https://stackoverflow.com/questions/79265537 预防措施:确保 React 19 与 @clerk/[email protected]+ 兼容
问题 #9:异步 auth() 辅助函数引发的困惑
错误:“auth() is not a function” 来源:https://clerk.com/changelog/2024-10-22-clerk-nextjs-v6 预防措施:始终使用 await:`const { userId } = await auth()`
问题 #10:环境变量配置错误
错误:“Missing Publishable Key” 或密钥泄露 预防措施:使用正确的前缀(`NEXT_PUBLIC_`、`VITE_`),切勿提交密钥
问题 #11:431 请求标头字段过大(Vite 开发模式)
错误:登录时出现“431 Request Header Fields Too Large” 来源:在 Vite 开发模式下测试自定义 JWT 声明时很常见 原因:URL 中的 Clerk `__clerk_handshake` 令牌超出 Vite 的 8KB 标头限制 预防措施:
在 `package.json` 中添加: ```json { "scripts": { "dev": "NODE_OPTIONS='--max-http-header-size=32768' vite" } } ```
临时解决方法:清除浏览器缓存,退出登录,然后重新登录
原因:Clerk 开发令牌比生产环境中的令牌更大;自定义 JWT 声明会增加握手令牌的大小
注意:这与问题 #6(会话令牌大小)不同。问题 #6 涉及 Cookie(1.2KB),而此问题涉及开发模式下的 URL 参数(8KB → 32KB)。
关键规则
始终执行
✅ 验证令牌时设置 `authorizedParties` ✅ 使用 `CLERK_SECRET_KEY` 环境变量 ✅ 渲染身份验证 UI 前检查 `isLoaded` ✅ 每次请求都使用最新的 `getToken()` ✅ 在 Next.js v6+ 中对 `auth()` 使用 await ✅ 仅对客户端变量使用 `NEXT_PUBLIC_` 前缀 ✅ 通过 `wrangler secret put` 存储密钥 ✅ 实现中间件以保护路由 ✅ 使用 2025-04-10 或更高版本的 API
绝不执行
❌ 在客户端代码中存储 `CLERK_SECRET_KEY` ❌ 使用已弃用的 `apiKey` 参数 ❌ 在 localStorage 中存储令牌 ❌ 跳过 `authorizedParties` 检查 ❌ 自定义 JWT 声明超过 1.2KB ❌ 忘记检查 `isLoaded` ❌ 使用 `NEXT_PUBLIC_` 前缀暴露密钥 ❌ 使用 API v1
官方文档
- Clerk 文档:https://clerk.com/docs
- Next.js 指南:https://clerk.com/docs/references/nextjs/overview
- React 指南:https://clerk.com/docs/references/react/overview
- 后端 SDK:https://clerk.com/docs/reference/backend/overview
- JWT 模板:https://clerk.com/docs/guides/sessions/jwt-templates
- shadcn/ui 集成:https://clerk.com/docs/guides/development/shadcn-cli
- Context7 库 ID:`/clerk/clerk-docs`
软件包版本(已于 2025-10-22 验证)
```json { "dependencies": { "@clerk/nextjs": "^6.33.3", "@clerk/clerk-react": "^5.51.0", "@clerk/backend": "^2.17.2" } } ```