mcpField
C
clerk-auth

clerk-auth

github.com/jezweb安全1.1k310 次安装3 周前更新
代码仓库

关于 clerk-auth

面向使用 Clerk 的开发者,汇总认证配置、版本变更与常见错误的预防指南。内容涵盖 Next.js、API 密钥、令牌验证和 Webhook 等实践。

主要功能

  1. 01Clerk 版本升级注意事项
  2. 02Next.js 异步认证配置
  3. 03API 密钥管理与验证
  4. 04中间件路由保护示例
  5. 05令牌与 Webhook 安全建议

适用场景

  1. 01将 Clerk 集成到 Next.js 应用
  2. 02排查升级后的认证错误
  3. 03验证 API 密钥和会话令牌
  4. 04配置公开 Webhook 路由

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/jezweb/claude-skills --skill clerk-auth

安装后在 Claude Code 中输入/clerk-auth确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

README.md
.claude-plugin/
plugin.json
agents/
clerk-setup.md
assets/
example-template.txt
commands/
setup.md
references/
common-errors.md
example-reference.md
jwt-claims-guide.md
testing-guide.md
rules/
clerk-auth.md
scripts/
example-script.sh
generate-session-token.js
templates/cloudflare/
worker-auth.ts
wrangler.jsonc
templates/env-examples/
.dev.vars.example
.env.local.example
.env.local.vite.example
templates/jwt/
advanced-template.json
basic-template.json
grafbase-template.json
supabase-template.json
templates/nextjs/
app-layout.tsx
middleware.ts
server-component-example.tsx
templates/react/
App.tsx
main.tsx
templates/typescript/
custom-jwt-types.d.ts
templates/vite/
package.json

详细介绍

Clerk Auth — 重大变更与错误预防指南

软件包版本:@clerk/[email protected]、@clerk/[email protected]、@clerk/[email protected]、@clerk/[email protected] 重大变更:2025 年 11 月 — API 版本 2025-11-10;2024 年 10 月 — Next.js v6 异步 auth() 最后更新:2026-01-09


新增内容(2025 年 12 月至 2026 年 1 月)

1. API 密钥 Beta 版(2025 年 12 月 11 日)— 全新 ✨

面向应用的用户级和组织级 API 密钥。提供零代码 UI 组件。

// 1. Add the component for self-service API key management
import { APIKeys } from '@clerk/nextjs'

export default function SettingsPage() {
  return (
    <div>
      <h2>API Keys</h2>
      <APIKeys />  {/* Full CRUD UI for user's API keys */}
    </div>
  )
}

后端验证:

import { verifyToken } from '@clerk/backend'

// API keys are verified like session tokens
const { data, error } = await verifyToken(apiKey, {
  secretKey: process.env.CLERK_SECRET_KEY,
  authorizedParties: ['https://yourdomain.com'],
})

// Check token type
if (data?.tokenType === 'api_key') {
  // Handle API key auth
}

clerkMiddleware 令牌类型:

// v6.36.0+: Middleware can distinguish token types
clerkMiddleware((auth, req) => {
  const { userId, tokenType } = auth()

  if (tokenType === 'api_key') {
    // API key auth - programmatic access
  } else if (tokenType === 'session_token') {
    // Regular session - web UI access
  }
})

定价(Beta 版免费):

  • 创建:$0.001/个密钥
  • 验证:$0.0001/次验证
2. Next.js 16:proxy.ts 中间件文件名(2025 年 12 月)

⚠️ 重大变更:由于严重安全漏洞(于 2025 年 3 月披露),Next.js 16 更改了中间件文件名。

背景:2025 年 3 月披露的漏洞(影响 Next.js 11.1.4-15.2.2)允许攻击者只需添加一个 HTTP 标头 x-middleware-subrequest: true,即可完全绕过基于中间件的授权。所有身份验证库(NextAuth、Clerk、自定义解决方案)均受到影响。

更名原因:middleware.ts → proxy.ts 的变更不只是表面上的调整——这是 Next.js 在表明,以中间件为先的安全模式存在风险。未来的身份验证实现不应仅依赖中间件进行授权。

Next.js 15 and earlier: middleware.ts
Next.js 16+:            proxy.ts

Next.js 16 的正确设置:

// src/proxy.ts (NOT middleware.ts!)
import { clerkMiddleware } from '@clerk/nextjs/server'

export default clerkMiddleware()

export const config = {
  matcher: [
    '/((?!_next|[^?]*\\.(?:html?|css|js(?!on)|jpe?g|webp|png|gif|svg|ttf|woff2?|ico|csv|docx?|xlsx?|zip|webmanifest)).*)',
    '/(api|trpc)(.*)',
  ],
}

最低版本:Next.js 16 需要 @clerk/[email protected]+(修复 Turbopack 构建错误和退出登录时的缓存失效问题)。

3. 强制重置密码(2025 年 12 月 19 日)

管理员可以将密码标记为已泄露,并强制重置:

import { clerkClient } from '@clerk/backend'

// Force password reset for a user
await clerkClient.users.updateUser(userId, {
  passwordDigest: 'compromised',  // Triggers reset on next sign-in
})
4. 组织报告与筛选条件(2025 年 12 月 15 日至 17 日)

控制面板现已包含组织创建指标,并可按名称、slug 和日期进行筛选。


API 版本 2025-11-10 的重大变更

1. API 版本 2025-11-10(2025 年 11 月 10 日)— 重大变更 ⚠️

**影响范围:**使用 Clerk Billing/Commerce API 的应用

关键变更:

  • 端点 URL:/commerce/ → /billing/(30 多个端点)

    GET /v1/commerce/plans → GET /v1/billing/plans
    GET /v1/commerce/statements → GET /v1/billing/statements
    POST /v1/me/commerce/checkouts → POST /v1/me/billing/checkouts
    
  • 字段术语:payment_source → payment_method

    // OLD (deprecated)
    { payment_source_id: "...", payment_source: {...} }
    
    // NEW (required)
    { payment_method_id: "...", payment_method: {...} }
    
  • **移除的字段:**Plans 响应不再包含:

    • amount、amount_formatted(改用 fee.amount)
    • currency、currency_symbol(改用 fee 对象)
    • payer_type(改用 for_payer_type)
    • annual_monthly_amount、annual_amount
  • 移除的端点:

    • Invoices 端点(改用 statements)
    • Products 端点
  • **空值处理:**明确规定——null 表示“不存在”,省略则表示“不声明是否存在”

**迁移:**将 SDK 更新至 v6.35.0+,该版本支持 API 版本 2025-11-10。

**官方指南:**https://clerk.com/docs/guides/development/upgrading/upgrade-guides/2025-11-10

2. Next.js v6 异步 auth()(2024 年 10 月)— 重大变更 ⚠️

**影响范围:**所有使用 auth() 的 Next.js 服务端组件

// ❌ OLD (v5 - synchronous)
const { userId } = auth()

// ✅ NEW (v6 - asynchronous)
const { userId } = await auth()

**也影响:**中间件中的 auth.protect() 现在是异步的

// ❌ OLD (v5)
auth.protect()

// ✅ NEW (v6)
await auth.protect()

**兼容性:**支持 Next.js 15、16。默认采用静态渲染。

3. 自定义 OAuth 的 PKCE 支持(2025 年 11 月 12 日)

自定义 OIDC 提供商和社交连接现已支持 PKCE(Proof Key for Code Exchange),可增强原生应用和移动应用的安全性,因为这类应用无法安全存储客户端密钥。

**适用场景:**移动应用、原生应用,以及无法安全存储密钥的公共客户端。

4. 客户端信任:防御凭据填充攻击(2025 年 11 月 14 日)

当用户从无法识别的设备登录时,自动启用二次身份验证:

  • 对密码有效但未启用 2FA 的用户生效
  • 无需配置
  • 所有 Clerk 方案均包含此功能

**工作原理:**检测到用户从新设备登录时,Clerk 会自动提示用户进行额外验证(电子邮件验证码、备用代码)。

5. 支持 Next.js 16(2025 年 11 月)

@clerk/nextjs v6.35.2+ 改进了用户退出登录时 Next.js 16 的缓存失效处理。


关键模式与错误预防

Next.js v6:异步 auth() 助手

模式:

import { auth } from '@clerk/nextjs/server'

export default async function Page() {
  const { userId } = await auth()  // ← Must await

  if (!userId) {
    return <div>Unauthorized</div>
  }

  return <div>User ID: {userId}</div>
}
Cloudflare Workers:authorizedParties(防止 CSRF)

重要: 始终设置 authorizedParties,以防止 CSRF 攻击

import { verifyToken } from '@clerk/backend'

const { data, error } = await verifyToken(token, {
  secretKey: c.env.CLERK_SECRET_KEY,
  // REQUIRED: Prevent CSRF attacks
  authorizedParties: ['https://yourdomain.com'],
})

原因: 如果不设置 authorizedParties,攻击者就可以使用来自其他域的有效令牌。

来源: https://clerk.com/docs/reference/backend/verify-token


clerkMiddleware() 配置

路由保护模式
import { clerkMiddleware, createRouteMatcher } from '@clerk/nextjs/server'

// Define protected routes
const isProtectedRoute = createRouteMatcher([
  '/dashboard(.*)',
  '/api/private(.*)',
])

const isAdminRoute = createRouteMatcher(['/admin(.*)'])

export default clerkMiddleware(async (auth, req) => {
  // Protect routes
  if (isProtectedRoute(req)) {
    await auth.protect()  // Redirects unauthenticated users
  }

  // Require specific permissions
  if (isAdminRoute(req)) {
    await auth.protect({
      role: 'org:admin',  // Requires organization admin role
    })
  }
})
所有中间件选项
选项类型说明
debugboolean启用调试日志
jwtKeystring用于无网络验证的 JWKS 公钥
clockSkewInMsnumber令牌时间偏差(默认:5000ms)
organizationSyncOptionsobject基于 URL 的组织激活
signInUrlstring自定义登录 URL
signUpUrlstring自定义注册 URL
组织同步(基于 URL 的组织激活)

⚠️ 仅限 Next.js:此功能目前仅适用于 Next.js 中的 clerkMiddleware()。由于会检查 Sec-Fetch-Dest 标头,因此无法在其他运行时(Cloudflare Workers、Express 等)中与 authenticateRequest() 搭配使用。

来源:GitHub Issue #7178

clerkMiddleware({
  organizationSyncOptions: {
    organizationPatterns: ['/orgs/:slug', '/orgs/:slug/(.*)'],
    personalAccountPatterns: ['/personal', '/personal/(.*)'],
  },
})

Webhook

Webhook 验证
import { Webhook } from 'svix'

export async function POST(req: Request) {
  const payload = await req.text()
  const headers = {
    'svix-id': req.headers.get('svix-id')!,
    'svix-timestamp': req.headers.get('svix-timestamp')!,
    'svix-signature': req.headers.get('svix-signature')!,
  }

  const wh = new Webhook(process.env.CLERK_WEBHOOK_SIGNING_SECRET!)

  try {
    const event = wh.verify(payload, headers)
    // Process event
    return Response.json({ success: true })
  } catch (err) {
    return Response.json({ error: 'Invalid signature' }, { status: 400 })
  }
}
常见事件类型
事件触发条件
user.created新用户注册
user.updated用户个人资料更改
user.deleted用户账户删除
session.created新登录
session.ended登出
organization.created新组织创建
organization.membership.created用户加入组织

⚠️ 重要: Webhook 路由必须公开(无需身份验证)。将其添加到中间件排除列表:

const isPublicRoute = createRouteMatcher([
  '/api/webhooks/clerk(.*)',  // Clerk webhooks are public
])

clerkMiddleware((auth, req) => {
  if (!isPublicRoute(req)) {
    auth.protect()
  }
})

UI 组件快速参考

组件用途
<SignIn />完整登录流程
<SignUp />完整注册流程
<SignInButton />触发登录弹窗
<SignUpButton />触发注册弹窗
<SignedIn>仅在已通过身份验证时渲染
<SignedOut>仅在未通过身份验证时渲染
<UserButton />包含登出功能的用户菜单
<UserProfile />完整的个人资料管理
<OrganizationSwitcher />切换组织
<OrganizationProfile />组织设置
<CreateOrganization />创建新组织
<APIKeys />API 密钥管理(新增)
React Hooks
Hook返回值
useAuth(){ userId, sessionId, isLoaded, isSignedIn, getToken }
useUser(){ user, isLoaded, isSignedIn }
useClerk()包含方法的 Clerk 实例
useSession()当前会话对象
useOrganization()当前组织上下文
useOrganizationList()用户的所有组织

JWT 模板 — 大小限制与短代码

JWT 大小限制:自定义声明为 1.2KB ⚠️

问题:浏览器 Cookie 限制为 4KB。Clerk 的默认声明占用约 2.8KB,因此留给自定义声明的空间为 1.2KB。

⚠️ 开发提示:在 Vite 开发模式下测试自定义 JWT 声明时,可能会遇到 “431 Request Header Fields Too Large” 错误。这是由于 URL 中的 Clerk 握手令牌超过了 Vite 的 8KB 限制。解决方法请参阅 Issue #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 中包含标识符/角色。

可用短代码参考
类别短代码示例
用户 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"

高级功能:

  • 字符串插值:"{{user.last_name}} {{user.first_name}}"
  • 条件回退值:"{{user.public_metadata.role || 'user'}}"
  • 嵌套元数据:"{{user.public_metadata.profile.interests}}"

官方文档:https://clerk.com/docs/guides/sessions/jwt-templates


使用 Clerk 进行测试

测试凭据(固定 OTP:424242)

测试邮箱(不会发送邮件,使用固定 OTP):

[email protected]
[email protected]

测试电话号码(不会发送短信,使用固定 OTP):

+12015550100
+19735550133

固定 OTP 代码:424242(适用于所有测试凭据)

生成会话令牌(有效期 60 秒)

脚本(scripts/generate-session-token.js):

# Generate token
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

手动流程:

  1. 创建用户:POST /v1/users
  2. 创建会话:POST /v1/sessions
  3. 生成令牌:POST /v1/sessions/{session_id}/tokens
  4. 在标头中使用: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')
})

官方文档:https://clerk.com/docs/guides/development/testing/overview


预防已知问题

此技能可预防 15 个有记录的问题:

问题 #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-11-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”(请求标头字段过大) 来源:使用自定义 JWT 声明进行测试时,在 Vite 开发模式中很常见 原因:URL 中的 Clerk __clerk_handshake 令牌超出 Vite 的 8KB 标头限制 预防措施:

添加到 package.json:

{
  "scripts": {
    "dev": "NODE_OPTIONS='--max-http-header-size=32768' vite"
  }
}

临时解决方法:清除浏览器缓存,退出登录,然后重新登录

原因:Clerk 开发令牌比生产环境中的令牌更大;自定义 JWT 声明会增加握手令牌的大小

注意:这与问题 #6(会话令牌大小)不同。问题 #6 涉及 Cookie(1.2KB),而此问题涉及开发模式下的 URL 参数(8KB → 32KB)。

问题 #12:用户类型不匹配(useUser 与 currentUser)

错误:在客户端/服务端之间共享用户工具时出现 TypeScript 错误 来源:GitHub Issue #2176 原因:useUser() 返回 UserResource(客户端),其属性与 currentUser() 返回的 User(服务端)不同。客户端有 fullName 和 primaryEmailAddress 对象;服务端则有 primaryEmailAddressId 和 privateMetadata。 预防措施:仅使用两端共有的属性,或为客户端和服务端上下文分别创建工具函数。

// ✅ CORRECT: Use properties that exist in both
const primaryEmailAddress = user.emailAddresses.find(
  ({ id }) => id === user.primaryEmailAddressId
)

// ✅ CORRECT: Separate types
type ClientUser = ReturnType<typeof useUser>['user']
type ServerUser = Awaited<ReturnType<typeof currentUser>>
问题 #13:多个 acceptsToken 类型导致 token-type-mismatch

错误:将 authenticateRequest() 与多种令牌类型搭配使用时出现“token-type-mismatch” 来源:GitHub Issue #7520 原因:将 authenticateRequest() 与多个 acceptsToken 值搭配使用时(例如 ['session_token', 'api_key']),Clerk 会错误地抛出 token-type-mismatch 错误。 预防措施:升级到 @clerk/[email protected]+(快照中已有修复,即将发布)。

// This now works in @clerk/[email protected]+
const result = await authenticateRequest(request, {
  acceptsToken: ['session_token', 'api_key'],  // Fixed!
})
问题 #14:deriveUrlFromHeaders 遇到格式错误的 URL 时导致服务端崩溃

错误:服务端因 URL 解析错误而崩溃 来源:GitHub Issue #7275 原因:内部 deriveUrlFromHeaders() 函数会进行不安全的 URL 解析,在标头中收到格式错误的 URL(例如 x-forwarded-host: 'example.com[invalid]')时导致整个服务端崩溃。这是一个拒绝服务漏洞。 预防措施:升级到 @clerk/[email protected]+(已修复)。

问题 #15:面向待处理会话的 treatPendingAsSignedOut 选项

错误:无——用于处理边缘情况的可选参数 来源:Changelog @clerk/[email protected] 存在原因:某些流程中(例如凭据填充防御的辅助身份验证),会话状态可能为 pending。默认情况下,待处理会话会被视为已退出登录(用户为 null)。 用法:设置 treatPendingAsSignedOut: false,将待处理状态视为已登录(适用于 @clerk/[email protected]+)。

// Default: pending = signed out
const user = await currentUser()  // null if status is 'pending'

// Treat pending as signed in
const user = await currentUser({ treatPendingAsSignedOut: false })  // defined if pending

生产环境注意事项

服务可用性与可靠性

背景:Clerk 在 2025 年 5 月至 6 月期间经历了 3 次重大服务中断,原因归咎于 Google Cloud Platform (GCP) 故障。2025 年 6 月 26 日的故障持续了 45 分钟(UTC 时间 6:16-7:01),影响了所有 Clerk 客户。

来源:Clerk 事后分析:2025 年 6 月 26 日

缓解策略:

  • 关注 Clerk 状态,获取实时更新
  • 在 Clerk API 不可用时实现优雅降级
  • 尽可能在本地缓存身份验证令牌
  • 对于现有会话,使用 jwtKey 选项进行无网络验证:
clerkMiddleware({
  jwtKey: process.env.CLERK_JWT_KEY,  // Allows offline token verification
})

注意:在完全中断期间,无法创建新会话(身份验证需要 Clerk API)。不过,如果使用 jwtKey 在本地验证 JWT,现有会话仍可继续使用。Clerk 承诺将探索多云冗余方案,以降低对单一供应商的依赖风险。


官方文档


软件包版本

最新版本(2025 年 11 月 22 日):

{
  "dependencies": {
    "@clerk/nextjs": "^6.36.7",
    "@clerk/clerk-react": "^5.59.2",
    "@clerk/backend": "^2.29.2",
    "@clerk/testing": "^1.13.26"
  }
}

令牌效率:

  • 不使用 skill:约 6,500 个令牌(设置教程、JWT 模板、测试设置、Webhooks、生产环境注意事项)
  • 使用 skill:约 3,200 个令牌(重大变更 + 关键模式 + 错误预防 + 生产环境指南)
  • 节省:约 51%(约 3,300 个令牌)

预防的错误:15 个已记录问题及其精确解决方案 核心价值:API Keys 测试版、Next.js 16 proxy.ts(附 2025 年 3 月 CVE 背景)、clerkMiddleware() 选项、Webhooks、组件参考、API 2025-11-10 重大变更、JWT 大小限制、用户类型不匹配、生产环境注意事项(GCP 故障、jwtKey 离线验证)


最后验证时间:2026-01-20 | Skill 版本:3.1.0 | 变更:新增 4 个已知问题(#12-15:用户类型不匹配、acceptsToken 类型不匹配、deriveUrlFromHeaders 崩溃、treatPendingAsSignedOut 选项),扩展 proxy.ts 部分,加入 2025 年 3 月 CVE 安全背景,新增生产环境注意事项部分(GCP 故障 + 缓解措施),加入 organizationSyncOptions 仅适用于 Next.js 的限制说明,更新 Next.js 16 的最低版本要求(6.35.0+)。

评论

支持 Markdown