clerk-auth

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Clerk Auth - Breaking Changes & Error Prevention Guide

Clerk 身份验证 - 破坏性变更与错误预防指南

Package Versions: @clerk/nextjs@6.36.7, @clerk/backend@2.29.2, @clerk/clerk-react@5.59.2, @clerk/testing@1.13.26 Breaking Changes: Nov 2025 - API version 2025-11-10, Oct 2024 - Next.js v6 async auth() Last Updated: 2026-01-09

包版本:@clerk/nextjs@6.36.7, @clerk/backend@2.29.2, @clerk/clerk-react@5.59.2, @clerk/testing@1.13.26 破坏性变更:2025年11月 - API版本2025-11-10,2024年10月 - Next.js v6 异步auth() 最后更新时间:2026-01-09

What's New (Dec 2025 - Jan 2026)

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

1. API Keys Beta (Dec 11, 2025) - NEW ✨

1. API Keys测试版(2025年12月11日)- 新增 ✨

User-scoped and organization-scoped API keys for your application. Zero-code UI component.
typescript
// 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>
  )
}
Backend Verification:
typescript
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 Token Types:
typescript
// 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
  }
})
Pricing (Beta = Free):
  • Creation: $0.001/key
  • Verification: $0.0001/verification
面向应用的用户级和组织级API密钥。零代码UI组件。
typescript
// 1. 添加用于自助式API密钥管理的组件
import { APIKeys } from '@clerk/nextjs'

export default function SettingsPage() {
  return (
    <div>
      <h2>API密钥</h2>
      <APIKeys />  {/* 用户API密钥的完整CRUD界面 */}
    </div>
  )
}
后端验证:
typescript
import { verifyToken } from '@clerk/backend'

// API密钥的验证方式与会话令牌相同
const { data, error } = await verifyToken(apiKey, {
  secretKey: process.env.CLERK_SECRET_KEY,
  authorizedParties: ['https://yourdomain.com'],
})

// 检查令牌类型
if (data?.tokenType === 'api_key') {
  // 处理API密钥身份验证
}
clerkMiddleware令牌类型:
typescript
// v6.36.0+:中间件可区分令牌类型
clerkMiddleware((auth, req) => {
  const { userId, tokenType } = auth()

  if (tokenType === 'api_key') {
    // API密钥身份验证 - 程序化访问
  } else if (tokenType === 'session_token') {
    // 常规会话 - Web界面访问
  }
})
定价(测试版免费):
  • 创建:0.001美元/密钥
  • 验证:0.0001美元/次验证

2. Next.js 16: proxy.ts Middleware Filename (Dec 2025)

2. Next.js 16:proxy.ts中间件文件名(2025年12月)

⚠️ BREAKING: Next.js 16 changed middleware filename due to critical security vulnerability (CVE disclosed March 2025).
Background: The March 2025 vulnerability (affecting Next.js 11.1.4-15.2.2) allowed attackers to completely bypass middleware-based authorization by adding a single HTTP header:
x-middleware-subrequest: true
. This affected all auth libraries (NextAuth, Clerk, custom solutions).
Why the Rename: The
middleware.ts
proxy.ts
change isn't just cosmetic - it's Next.js signaling that middleware-first security patterns are dangerous. Future auth implementations should not rely solely on middleware for authorization.
Next.js 15 and earlier: middleware.ts
Next.js 16+:            proxy.ts
Correct Setup for Next.js 16:
typescript
// 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)(.*)',
  ],
}
Minimum Version: @clerk/nextjs@6.35.0+ required for Next.js 16 (fixes Turbopack build errors and cache invalidation on sign-out).
⚠️ 破坏性变更:由于严重安全漏洞(2025年3月披露的CVE),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及更早版本:middleware.ts
Next.js 16及以上版本:proxy.ts
Next.js 16正确配置:
typescript
// src/proxy.ts(不是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)(.*)',
  ],
}
最低版本要求:@clerk/nextjs@6.35.0+ 支持Next.js 16(修复了Turbopack构建错误和登出时的缓存失效问题)。

3. Force Password Reset (Dec 19, 2025)

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

Administrators can mark passwords as compromised and force reset:
typescript
import { clerkClient } from '@clerk/backend'

// Force password reset for a user
await clerkClient.users.updateUser(userId, {
  passwordDigest: 'compromised',  // Triggers reset on next sign-in
})
管理员可标记密码为已泄露并强制用户重置:
typescript
import { clerkClient } from '@clerk/backend'

// 强制用户重置密码
await clerkClient.users.updateUser(userId, {
  passwordDigest: 'compromised',  // 触发下次登录时的重置流程
})

4. Organization Reports & Filters (Dec 15-17, 2025)

4. 组织报告与筛选(2025年12月15-17日)

Dashboard now includes org creation metrics and filtering by name/slug/date.

控制台现在包含组织创建指标,支持按名称/别名/日期筛选。

API Version 2025-11-10 Breaking Changes

API版本2025-11-10破坏性变更

1. API Version 2025-11-10 (Nov 10, 2025) - BREAKING CHANGES ⚠️

1. API版本2025-11-10(2025年11月10日)- 破坏性变更 ⚠️

Affects: Applications using Clerk Billing/Commerce APIs
Critical Changes:
  • Endpoint URLs:
    /commerce/
    /billing/
    (30+ endpoints)
    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
  • Field Terminology:
    payment_source
    payment_method
    typescript
    // OLD (deprecated)
    { payment_source_id: "...", payment_source: {...} }
    
    // NEW (required)
    { payment_method_id: "...", payment_method: {...} }
  • Removed Fields: Plans responses no longer include:
    • amount
      ,
      amount_formatted
      (use
      fee.amount
      instead)
    • currency
      ,
      currency_symbol
      (use fee objects)
    • payer_type
      (use
      for_payer_type
      )
    • annual_monthly_amount
      ,
      annual_amount
  • Removed Endpoints:
    • Invoices endpoint (use statements)
    • Products endpoint
  • Null Handling: Explicit rules -
    null
    means "doesn't exist", omitted means "not asserting existence"
Migration: Update SDK to v6.35.0+ which includes support for API version 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
    typescript
    // 旧版(已废弃)
    { payment_source_id: "...", payment_source: {...} }
    
    // 新版(必填)
    { payment_method_id: "...", payment_method: {...} }
  • 移除的字段:计划响应不再包含以下字段:
    • amount
      ,
      amount_formatted
      (改用
      fee.amount
    • currency
      ,
      currency_symbol
      (改用fee对象)
    • payer_type
      (改用
      for_payer_type
    • annual_monthly_amount
      ,
      annual_amount
  • 移除的端点
    • 发票端点(改用对账单)
    • 产品端点
  • Null值处理:明确规则 -
    null
    表示“不存在”,省略表示“不声明存在性”
迁移建议:将SDK更新至v6.35.0+,该版本支持API版本2025-11-10。

2. Next.js v6 Async auth() (Oct 2024) - BREAKING CHANGE ⚠️

2. Next.js v6 异步auth()(2024年10月)- 破坏性变更 ⚠️

Affects: All Next.js Server Components using
auth()
typescript
// ❌ OLD (v5 - synchronous)
const { userId } = auth()

// ✅ NEW (v6 - asynchronous)
const { userId } = await auth()
Also affects:
auth.protect()
is now async in middleware
typescript
// ❌ OLD (v5)
auth.protect()

// ✅ NEW (v6)
await auth.protect()
Compatibility: Next.js 15, 16 supported. Static rendering by default.
影响范围:所有使用
auth()
的Next.js服务器组件
typescript
// ❌ 旧版(v5 - 同步)
const { userId } = auth()

// ✅ 新版(v6 - 异步)
const { userId } = await auth()
同时影响
auth.protect()
在中间件中现在也是异步的
typescript
// ❌ 旧版(v5)
auth.protect()

// ✅ 新版(v6)
await auth.protect()
兼容性:支持Next.js 15、16。默认启用静态渲染。

3. PKCE Support for Custom OAuth (Nov 12, 2025)

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

Custom OIDC providers and social connections now support PKCE (Proof Key for Code Exchange) for enhanced security in native/mobile applications where client secrets cannot be safely stored.
Use case: Mobile apps, native apps, public clients that can't securely store secrets.
自定义OIDC提供商和社交连接现在支持PKCE(授权码交换证明密钥),增强了原生/移动应用的安全性,此类应用无法安全存储客户端密钥。
适用场景:移动应用、原生应用、无法安全存储密钥的公共客户端。

4. Client Trust: Credential Stuffing Defense (Nov 14, 2025)

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

Automatic secondary authentication when users sign in from unrecognized devices:
  • Activates for users with valid passwords but no 2FA
  • No configuration required
  • Included in all Clerk plans
How it works: Clerk automatically prompts for additional verification (email code, backup code) when detecting sign-in from new device.
当用户从未识别设备登录时,自动触发二次身份验证:
  • 适用于拥有有效密码但未启用2FA的用户
  • 无需配置
  • 所有Clerk套餐均包含
工作原理:Clerk检测到新设备登录时,自动提示进行额外验证(邮件验证码、备用验证码)。

5. Next.js 16 Support (Nov 2025)

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

@clerk/nextjs v6.35.2+ includes cache invalidation improvements for Next.js 16 during sign-out.

@clerk/nextjs v6.35.2+ 包含Next.js 16登出时的缓存失效改进。

Critical Patterns & Error Prevention

关键模式与错误预防

Next.js v6: Async auth() Helper

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

Pattern:
typescript
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>
}
模式示例:
typescript
import { auth } from '@clerk/nextjs/server'

export default async function Page() {
  const { userId } = await auth()  // ← 必须使用await

  if (!userId) {
    return <div>未授权</div>
  }

  return <div>用户ID{userId}</div>
}

Cloudflare Workers: authorizedParties (CSRF Prevention)

Cloudflare Workers:authorizedParties(CSRF预防)

CRITICAL: Always set
authorizedParties
to prevent CSRF attacks
typescript
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'],
})
Why: Without
authorizedParties
, attackers can use valid tokens from other domains.

至关重要:始终设置
authorizedParties
以防止CSRF攻击
typescript
import { verifyToken } from '@clerk/backend'

const { data, error } = await verifyToken(token, {
  secretKey: c.env.CLERK_SECRET_KEY,
  // 必填:防止CSRF攻击
  authorizedParties: ['https://yourdomain.com'],
})
原因:如果不设置
authorizedParties
,攻击者可使用来自其他域名的有效令牌。

clerkMiddleware() Configuration

clerkMiddleware()配置

Route Protection Patterns

路由保护模式

typescript
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
    })
  }
})
typescript
import { clerkMiddleware, createRouteMatcher } from '@clerk/nextjs/server'

// 定义受保护路由
const isProtectedRoute = createRouteMatcher([
  '/dashboard(.*)',
  '/api/private(.*)',
])

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

export default clerkMiddleware(async (auth, req) => {
  // 保护路由
  if (isProtectedRoute(req)) {
    await auth.protect()  // 将未认证用户重定向
  }

  // 要求特定权限
  if (isAdminRoute(req)) {
    await auth.protect({
      role: 'org:admin',  // 需要组织管理员角色
    })
  }
})

All Middleware Options

所有中间件选项

OptionTypeDescription
debug
boolean
Enable debug logging
jwtKey
string
JWKS public key for networkless verification
clockSkewInMs
number
Token time variance (default: 5000ms)
organizationSyncOptions
object
URL-based org activation
signInUrl
string
Custom sign-in URL
signUpUrl
string
Custom sign-up URL
选项类型描述
debug
boolean
启用调试日志
jwtKey
string
用于无网络验证的JWKS公钥
clockSkewInMs
number
令牌时间偏差(默认:5000ms)
organizationSyncOptions
object
基于URL的组织激活
signInUrl
string
自定义登录URL
signUpUrl
string
自定义注册URL

Organization Sync (URL-based Org Activation)

组织同步(基于URL的组织激活)

⚠️ Next.js Only: This feature currently only works with
clerkMiddleware()
in Next.js. It does NOT work with
authenticateRequest()
in other runtimes (Cloudflare Workers, Express, etc.) due to
Sec-Fetch-Dest
header checks.
typescript
clerkMiddleware({
  organizationSyncOptions: {
    organizationPatterns: ['/orgs/:slug', '/orgs/:slug/(.*)'],
    personalAccountPatterns: ['/personal', '/personal/(.*)'],
  },
})

⚠️ 仅Next.js支持:此功能目前仅适用于Next.js中的
clerkMiddleware()
。由于
Sec-Fetch-Dest
头检查,它在其他运行时(Cloudflare Workers、Express等)的
authenticateRequest()
中无法工作。
typescript
clerkMiddleware({
  organizationSyncOptions: {
    organizationPatterns: ['/orgs/:slug', '/orgs/:slug/(.*)'],
    personalAccountPatterns: ['/personal', '/personal/(.*)'],
  },
})

Webhooks

Webhook

Webhook Verification

Webhook验证

typescript
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 })
  }
}
typescript
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)
    // 处理事件
    return Response.json({ success: true })
  } catch (err) {
    return Response.json({ error: '无效签名' }, { status: 400 })
  }
}

Common Event Types

常见事件类型

EventTrigger
user.created
New user signs up
user.updated
User profile changes
user.deleted
User account deleted
session.created
New sign-in
session.ended
Sign-out
organization.created
New org created
organization.membership.created
User joins org
⚠️ Important: Webhook routes must be PUBLIC (no auth). Add to middleware exclude list:
typescript
const isPublicRoute = createRouteMatcher([
  '/api/webhooks/clerk(.*)',  // Clerk webhooks are public
])

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

事件触发条件
user.created
新用户注册
user.updated
用户资料变更
user.deleted
用户账户删除
session.created
新登录会话
session.ended
用户登出
organization.created
新组织创建
organization.membership.created
用户加入组织
⚠️ 重要提示:Webhook路由必须设为公开(无需身份验证)。添加到中间件排除列表:
typescript
const isPublicRoute = createRouteMatcher([
  '/api/webhooks/clerk(.*)',  // Clerk Webhook为公开路由
])

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

UI Components Quick Reference

UI组件快速参考

ComponentPurpose
<SignIn />
Full sign-in flow
<SignUp />
Full sign-up flow
<SignInButton />
Trigger sign-in modal
<SignUpButton />
Trigger sign-up modal
<SignedIn>
Render only when authenticated
<SignedOut>
Render only when unauthenticated
<UserButton />
User menu with sign-out
<UserProfile />
Full profile management
<OrganizationSwitcher />
Switch between orgs
<OrganizationProfile />
Org settings
<CreateOrganization />
Create new org
<APIKeys />
API key management (NEW)
组件用途
<SignIn />
完整登录流程
<SignUp />
完整注册流程
<SignInButton />
触发登录模态框
<SignUpButton />
触发注册模态框
<SignedIn>
仅在用户已认证时渲染
<SignedOut>
仅在用户未认证时渲染
<UserButton />
包含登出选项的用户菜单
<UserProfile />
完整资料管理界面
<OrganizationSwitcher />
在组织间切换
<OrganizationProfile />
组织设置界面
<CreateOrganization />
创建新组织
<APIKeys />
API密钥管理(新增)

React Hooks

React钩子

HookReturns
useAuth()
{ userId, sessionId, isLoaded, isSignedIn, getToken }
useUser()
{ user, isLoaded, isSignedIn }
useClerk()
Clerk instance with methods
useSession()
Current session object
useOrganization()
Current org context
useOrganizationList()
All user's orgs

钩子返回值
useAuth()
{ userId, sessionId, isLoaded, isSignedIn, getToken }
useUser()
{ user, isLoaded, isSignedIn }
useClerk()
包含方法的Clerk实例
useSession()
当前会话对象
useOrganization()
当前组织上下文
useOrganizationList()
用户所属的所有组织

JWT Templates - Size Limits & Shortcodes

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

JWT Size Limitation: 1.2KB for Custom Claims ⚠️

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

Problem: Browser cookies limited to 4KB. Clerk's default claims consume ~2.8KB, leaving 1.2KB for custom claims.
⚠️ Development Note: When testing custom JWT claims in Vite dev mode, you may encounter "431 Request Header Fields Too Large" error. This is caused by Clerk's handshake token in the URL exceeding Vite's 8KB limit. See Issue #11 for solution.
Solution:
json
// ✅ 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
}
Best Practice: Store large data in database, include only identifiers/roles in JWT.
问题:浏览器Cookie限制为4KB。Clerk的默认声明占用约2.8KB,剩余1.2KB用于自定义声明
⚠️ 开发注意事项:在Vite开发模式下测试自定义JWT声明时,可能会遇到**"431 Request Header Fields Too Large"**错误。这是由于URL中的Clerk握手令牌超过了Vite的8KB限制。解决方案见问题#11
解决方案:
json
// ✅ 推荐:最小化声明
{
  "user_id": "{{user.id}}",
  "email": "{{user.primary_email_address}}",
  "role": "{{user.public_metadata.role}}"
}

// ❌ 不推荐:超出限制
{
  "bio": "{{user.public_metadata.bio}}",  // 6KB字段
  "all_metadata": "{{user.public_metadata}}"  // 整个对象
}
最佳实践:将大型数据存储在数据库中,仅在JWT中包含标识符/角色。

Available Shortcodes Reference

可用短代码参考

CategoryShortcodesExample
User ID & Name
{{user.id}}
,
{{user.first_name}}
,
{{user.last_name}}
,
{{user.full_name}}
"John Doe"
Contact
{{user.primary_email_address}}
,
{{user.primary_phone_address}}
"john@example.com"
Profile
{{user.image_url}}
,
{{user.username}}
,
{{user.created_at}}
"https://..."
Verification
{{user.email_verified}}
,
{{user.phone_number_verified}}
true
Metadata
{{user.public_metadata}}
,
{{user.public_metadata.FIELD}}
{"role": "admin"}
Organization
org_id
,
org_slug
,
org_role
(in sessionClaims)
"org:admin"
Advanced Features:
  • String Interpolation:
    "{{user.last_name}} {{user.first_name}}"
  • Conditional Fallbacks:
    "{{user.public_metadata.role || 'user'}}"
  • Nested Metadata:
    "{{user.public_metadata.profile.interests}}"

类别短代码示例
用户ID与名称
{{user.id}}
,
{{user.first_name}}
,
{{user.last_name}}
,
{{user.full_name}}
"John Doe"
联系方式
{{user.primary_email_address}}
,
{{user.primary_phone_address}}
"john@example.com"
资料信息
{{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}}"

Testing with Clerk

使用Clerk进行测试

Test Credentials (Fixed OTP: 424242)

测试凭证(固定OTP:424242)

Test Emails (no emails sent, fixed OTP):
john+clerk_test@example.com
jane+clerk_test@gmail.com
Test Phone Numbers (no SMS sent, fixed OTP):
+12015550100
+19735550133
Fixed OTP Code:
424242
(works for all test credentials)
测试邮箱(不发送邮件,固定OTP):
john+clerk_test@example.com
jane+clerk_test@gmail.com
测试手机号(不发送短信,固定OTP):
+12015550100
+19735550133
固定OTP验证码
424242
(适用于所有测试凭证)

Generate Session Tokens (60-second lifetime)

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

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

Generate token

生成令牌

CLERK_SECRET_KEY=sk_test_... node scripts/generate-session-token.js
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
CLERK_SECRET_KEY=sk_test_... node scripts/generate-session-token.js --create-user

Auto-refresh token every 50 seconds

每50秒自动刷新令牌

CLERK_SECRET_KEY=sk_test_... node scripts/generate-session-token.js --refresh

**Manual Flow**:
1. Create user: `POST /v1/users`
2. Create session: `POST /v1/sessions`
3. Generate token: `POST /v1/sessions/{session_id}/tokens`
4. Use in header: `Authorization: Bearer <token>`
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>`

E2E Testing with Playwright

使用Playwright进行端到端测试

Install
@clerk/testing
for automatic Testing Token management:
bash
npm install -D @clerk/testing
Global Setup (
global.setup.ts
):
typescript
import { clerkSetup } from '@clerk/testing/playwright'
import { test as setup } from '@playwright/test'

setup('global setup', async ({}) => {
  await clerkSetup()
})
Test File (
auth.spec.ts
):
typescript
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"]', 'test+clerk_test@example.com')
  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')
})

安装
@clerk/testing
以自动管理测试令牌:
bash
npm install -D @clerk/testing
全局设置
global.setup.ts
):
typescript
import { clerkSetup } from '@clerk/testing/playwright'
import { test as setup } from '@playwright/test'

setup('全局设置', async ({}) => {
  await clerkSetup()
})
测试文件
auth.spec.ts
):
typescript
import { setupClerkTestingToken } from '@clerk/testing/playwright'
import { test } from '@playwright/test'

test('注册', async ({ page }) => {
  await setupClerkTestingToken({ page })

  await page.goto('/sign-up')
  await page.fill('input[name="emailAddress"]', 'test+clerk_test@example.com')
  await page.fill('input[name="password"]', 'TestPassword123!')
  await page.click('button[type="submit"]')

  // 使用固定OTP验证
  await page.fill('input[name="code"]', '424242')
  await page.click('button[type="submit"]')

  await expect(page).toHaveURL('/dashboard')
})

Known Issues Prevention

已知问题预防

This skill prevents 15 documented issues:
本指南可预防15个已记录的问题

Issue #1: Missing Clerk Secret Key

问题#1:缺少Clerk密钥

Error: "Missing Clerk Secret Key or API Key" Source: https://stackoverflow.com/questions/77620604 Prevention: Always set in
.env.local
or via
wrangler secret put
错误信息:"Missing Clerk Secret Key or API Key" 来源https://stackoverflow.com/questions/77620604 预防措施:始终在
.env.local
中设置,或通过
wrangler secret put
配置

Issue #2: API Key → Secret Key Migration

问题#2:API密钥→密钥迁移

Error: "apiKey is deprecated, use secretKey" Source: https://clerk.com/docs/upgrade-guides/core-2/backend Prevention: Replace
apiKey
with
secretKey
in all calls
错误信息:"apiKey is deprecated, use secretKey" 来源https://clerk.com/docs/upgrade-guides/core-2/backend 预防措施:在所有调用中将
apiKey
替换为
secretKey

Issue #3: JWKS Cache Race Condition

问题#3:JWKS缓存竞态条件

Error: "No JWK available" Source: https://github.com/clerk/javascript/blob/main/packages/backend/CHANGELOG.md Prevention: Use @clerk/backend@2.17.2 or later (fixed)
错误信息:"No JWK available" 来源https://github.com/clerk/javascript/blob/main/packages/backend/CHANGELOG.md 预防措施:使用@clerk/backend@2.17.2或更高版本(已修复)

Issue #4: Missing authorizedParties (CSRF)

问题#4:缺少authorizedParties(CSRF)

Error: No error, but CSRF vulnerability Source: https://clerk.com/docs/reference/backend/verify-token Prevention: Always set
authorizedParties: ['https://yourdomain.com']
错误信息:无错误,但存在CSRF漏洞 来源https://clerk.com/docs/reference/backend/verify-token 预防措施:始终设置
authorizedParties: ['https://yourdomain.com']

Issue #5: Import Path Changes (Core 2)

问题#5:导入路径变更(Core 2)

Error: "Cannot find module" Source: https://clerk.com/docs/upgrade-guides/core-2/backend Prevention: Update import paths for Core 2
错误信息:"Cannot find module" 来源https://clerk.com/docs/upgrade-guides/core-2/backend 预防措施:更新Core 2的导入路径

Issue #6: JWT Size Limit Exceeded

问题#6:JWT大小限制超出

Error: Token exceeds size limit Source: https://clerk.com/docs/backend-requests/making/custom-session-token Prevention: Keep custom claims under 1.2KB
错误信息:令牌超出大小限制 来源https://clerk.com/docs/backend-requests/making/custom-session-token 预防措施:自定义声明保持在1.2KB以内

Issue #7: Deprecated API Version v1

问题#7:已废弃的API版本v1

Error: "API version v1 is deprecated" Source: https://clerk.com/docs/upgrade-guides/core-2/backend Prevention: Use latest SDK versions (API v2025-11-10)
错误信息:"API version v1 is deprecated" 来源https://clerk.com/docs/upgrade-guides/core-2/backend 预防措施:使用最新SDK版本(API v2025-11-10)

Issue #8: ClerkProvider JSX Component Error

问题#8:ClerkProvider JSX组件错误

Error: "cannot be used as a JSX component" Source: https://stackoverflow.com/questions/79265537 Prevention: Ensure React 19 compatibility with @clerk/clerk-react@5.59.2+
错误信息:"cannot be used as a JSX component" 来源https://stackoverflow.com/questions/79265537 预防措施:确保React 19与@clerk/clerk-react@5.59.2+兼容

Issue #9: Async auth() Helper Confusion

问题#9:异步auth()助手混淆

Error: "auth() is not a function" Source: https://clerk.com/changelog/2024-10-22-clerk-nextjs-v6 Prevention: Always await:
const { userId } = await auth()
错误信息:"auth() is not a function" 来源https://clerk.com/changelog/2024-10-22-clerk-nextjs-v6 预防措施:始终使用await:
const { userId } = await auth()

Issue #10: Environment Variable Misconfiguration

问题#10:环境变量配置错误

Error: "Missing Publishable Key" or secret leaked Prevention: Use correct prefixes (
NEXT_PUBLIC_
,
VITE_
), never commit secrets
错误信息:"Missing Publishable Key" 或密钥泄露 预防措施:使用正确的前缀(
NEXT_PUBLIC_
,
VITE_
),绝不提交密钥到版本控制系统

Issue #11: 431 Request Header Fields Too Large (Vite Dev Mode)

问题#11:431 Request Header Fields Too Large(Vite开发模式)

Error: "431 Request Header Fields Too Large" when signing in Source: Common in Vite dev mode when testing custom JWT claims Cause: Clerk's
__clerk_handshake
token in URL exceeds Vite's 8KB header limit Prevention:
Add to
package.json
:
json
{
  "scripts": {
    "dev": "NODE_OPTIONS='--max-http-header-size=32768' vite"
  }
}
Temporary Workaround: Clear browser cache, sign out, sign back in
Why: Clerk dev tokens are larger than production; custom JWT claims increase handshake token size
Note: This is different from Issue #6 (session token size). Issue #6 is about cookies (1.2KB), this is about URL parameters in dev mode (8KB → 32KB).
错误信息:登录时出现"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)。

Issue #12: User Type Mismatch (useUser vs currentUser)

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

Error: TypeScript errors when sharing user utilities across client/server Source: GitHub Issue #2176 Why It Happens:
useUser()
returns
UserResource
(client-side) with different properties than
currentUser()
returns
User
(server-side). Client has
fullName
,
primaryEmailAddress
object; server has
primaryEmailAddressId
and
privateMetadata
instead. Prevention: Use shared properties only, or create separate utility functions for client vs server contexts.
typescript
// ✅ 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>>
错误信息:在客户端/服务器之间共享用户工具时出现TypeScript错误 来源GitHub Issue #2176 原因
useUser()
返回
UserResource
(客户端),其属性与
currentUser()
返回的
User
(服务器端)不同。客户端有
fullName
primaryEmailAddress
对象;服务器端有
primaryEmailAddressId
privateMetadata
预防措施:仅使用共享属性,或为客户端和服务器上下文创建单独的工具函数。
typescript
// ✅ 正确:使用双方都存在的属性
const primaryEmailAddress = user.emailAddresses.find(
  ({ id }) => id === user.primaryEmailAddressId
)

// ✅ 正确:分离类型
type ClientUser = ReturnType<typeof useUser>['user']
type ServerUser = Awaited<ReturnType<typeof currentUser>>

Issue #13: Multiple acceptsToken Types Causes token-type-mismatch

问题#13:多种acceptsToken类型导致token-type-mismatch

Error: "token-type-mismatch" when using
authenticateRequest()
with multiple token types Source: GitHub Issue #7520 Why It Happens: When using
authenticateRequest()
with multiple
acceptsToken
values (e.g.,
['session_token', 'api_key']
), Clerk incorrectly throws token-type-mismatch error. Prevention: Upgrade to @clerk/backend@2.29.2+ (fix available in snapshot, releasing soon).
typescript
// This now works in @clerk/backend@2.29.2+
const result = await authenticateRequest(request, {
  acceptsToken: ['session_token', 'api_key'],  // Fixed!
})
错误信息:使用
authenticateRequest()
并设置多种令牌类型时出现"token-type-mismatch" 来源GitHub Issue #7520 原因:当
authenticateRequest()
设置多个
acceptsToken
值(如
['session_token', 'api_key']
)时,Clerk会错误地抛出令牌类型不匹配错误。 预防措施:升级到@clerk/backend@2.29.2+(修复已在快照版本中提供,即将正式发布)。
typescript
// @clerk/backend@2.29.2+ 中此问题已修复
const result = await authenticateRequest(request, {
  acceptsToken: ['session_token', 'api_key'],  // 已修复!
})

Issue #14: deriveUrlFromHeaders Server Crash on Malformed URLs

问题#14:deriveUrlFromHeaders在畸形URL下导致服务器崩溃

Error: Server crashes with URL parsing error Source: GitHub Issue #7275 Why It Happens: Internal
deriveUrlFromHeaders()
function performs unsafe URL parsing and crashes the entire server when receiving malformed URLs in headers (e.g.,
x-forwarded-host: 'example.com[invalid]'
). This is a denial-of-service vulnerability. Prevention: Upgrade to @clerk/backend@2.29.0+ (fixed).
错误信息:服务器因URL解析错误崩溃 来源GitHub Issue #7275 原因:内部
deriveUrlFromHeaders()
函数执行不安全的URL解析,当收到头中的畸形URL(如
x-forwarded-host: 'example.com[invalid]'
)时,会导致整个服务器崩溃。这是一个拒绝服务漏洞。 预防措施:升级到@clerk/backend@2.29.0+(已修复)。

Issue #15: treatPendingAsSignedOut Option for Pending Sessions

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

Error: None - optional parameter for edge case handling Source: Changelog @clerk/nextjs@6.32.0 Why It Exists: Sessions can have a
pending
status during certain flows (e.g., credential stuffing defense secondary auth). By default, pending sessions are treated as signed-out (user is null). Usage: Set
treatPendingAsSignedOut: false
to treat pending as signed-in (available in @clerk/nextjs@6.32.0+).
typescript
// 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

错误信息:无错误 - 边缘情况处理的可选参数 来源Changelog @clerk/nextjs@6.32.0 存在原因:在某些流程中(如凭证填充防御二次验证),会话可能处于
pending
状态。默认情况下,待处理会话会被视为已登出(用户为null)。 用法:设置
treatPendingAsSignedOut: false
将待处理会话视为已登录(@clerk/nextjs@6.32.0+支持)。
typescript
// 默认:待处理 = 已登出
const user = await currentUser()  // 如果状态为'pending',返回null

// 将待处理视为已登录
const user = await currentUser({ treatPendingAsSignedOut: false })  // 如果待处理则返回用户

Production Considerations

生产环境注意事项

Service Availability & Reliability

服务可用性与可靠性

Context: Clerk experienced 3 major service disruptions in May-June 2025 attributed to Google Cloud Platform (GCP) outages. The June 26, 2025 outage lasted 45 minutes (6:16-7:01 UTC) and affected all Clerk customers.
Mitigation Strategies:
  • Monitor Clerk Status for real-time updates
  • Implement graceful degradation when Clerk API is unavailable
  • Cache auth tokens locally where possible
  • For existing sessions, use
    jwtKey
    option for networkless verification:
typescript
clerkMiddleware({
  jwtKey: process.env.CLERK_JWT_KEY,  // Allows offline token verification
})
Note: During total outage, no new sessions can be created (auth requires Clerk API). However, existing sessions can continue working if you verify JWTs locally with
jwtKey
. Clerk committed to exploring multi-cloud redundancy to reduce single-vendor dependency risk.

背景:2025年5-6月,Clerk因Google Cloud Platform(GCP)故障经历了3次重大服务中断。2025年6月26日的中断持续了45分钟(UTC时间6:16-7:01),影响了所有Clerk客户。
缓解策略
  • 监控Clerk状态页获取实时更新
  • 当Clerk API不可用时,实现优雅降级
  • 尽可能在本地缓存身份验证令牌
  • 对于现有会话,使用
    jwtKey
    选项进行无网络验证:
typescript
clerkMiddleware({
  jwtKey: process.env.CLERK_JWT_KEY,  // 支持离线令牌验证
})
注意:在完全中断期间,无法创建新会话(身份验证需要Clerk API)。但如果使用
jwtKey
本地验证JWT,现有会话可继续工作。Clerk承诺探索多云冗余以降低单一供应商依赖风险。

Official Documentation

官方文档

Package Versions

包版本

Latest (Nov 22, 2025):
json
{
  "dependencies": {
    "@clerk/nextjs": "^6.36.7",
    "@clerk/clerk-react": "^5.59.2",
    "@clerk/backend": "^2.29.2",
    "@clerk/testing": "^1.13.26"
  }
}

Token Efficiency:
  • Without skill: ~6,500 tokens (setup tutorials, JWT templates, testing setup, webhooks, production considerations)
  • With skill: ~3,200 tokens (breaking changes + critical patterns + error prevention + production guidance)
  • Savings: ~51% (~3,300 tokens)
Errors prevented: 15 documented issues with exact solutions Key value: API Keys beta, Next.js 16 proxy.ts (with March 2025 CVE context), clerkMiddleware() options, webhooks, component reference, API 2025-11-10 breaking changes, JWT size limits, user type mismatches, production considerations (GCP outages, jwtKey offline verification)

Last verified: 2026-01-20 | Skill version: 3.1.0 | Changes: Added 4 new Known Issues (#12-15: user type mismatch, acceptsToken type mismatch, deriveUrlFromHeaders crash, treatPendingAsSignedOut option), expanded proxy.ts section with March 2025 CVE security context, added Production Considerations section (GCP outages + mitigation), added organizationSyncOptions Next.js-only limitation note, updated minimum version requirements for Next.js 16 (6.35.0+).
最新版本(2025年11月22日):
json
{
  "dependencies": {
    "@clerk/nextjs": "^6.36.7",
    "@clerk/clerk-react": "^5.59.2",
    "@clerk/backend": "^2.29.2",
    "@clerk/testing": "^1.13.26"
  }
}

令牌效率
  • 未使用本指南:约6500令牌(设置教程、JWT模板、测试设置、Webhook、生产注意事项)
  • 使用本指南:约3200令牌(破坏性变更+关键模式+错误预防+生产指导)
  • 节省:约51%(约3300令牌)
预防的错误:15个带确切解决方案的已记录问题 核心价值:API Keys测试版、Next.js 16 proxy.ts(含2025年3月CVE安全背景)、clerkMiddleware()选项、Webhook、组件参考、API 2025-11-10破坏性变更、JWT大小限制、用户类型不匹配、生产环境注意事项(GCP故障、jwtKey离线验证)

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