mcpField
C
clerk-auth

clerk-auth

github.com/jackspace安全1637 次安装12 周前更新
代码仓库

关于 clerk-auth

clerk-auth 是 Clerk 身份验证的实现指南,面向使用 React、Next.js 或 Cloudflare Workers 的开发者。内容涵盖应用配置、路由保护、令牌验证、自定义 JWT 声明和测试。

主要功能

  1. 01配置 React 和 Next.js 身份验证
  2. 02保护应用路由
  3. 03在 Cloudflare Workers 验证令牌
  4. 04创建自定义 JWT 声明
  5. 05使用测试凭据进行验证

适用场景

  1. 01为 React 或 Next.js 应用加入登录
  2. 02限制未授权请求访问路由
  3. 03在 Cloudflare Workers 检查会话令牌
  4. 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) 设置

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>, ) ```

重要:

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

Loading...
}

if (!isSignedIn) { return <button onClick={() => openSignIn()}>Sign In }

return (

Welcome {user.firstName}!

Email: {user.primaryEmailAddress?.emailAddress}

<button onClick={() => signOut()}>Sign Out
) } ```

重要原因:


Next.js App Router 设置

1. 安装 Clerk

```bash npm install @clerk/nextjs ```

最新版本:@clerk/[email protected](已于 2025-10-22 验证)

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 ```

重要:

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)(.*)',
  ],
}

重要:

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

最新版本:

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

关键:


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

手动流程:

  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')
})
测试令牌(绕过机器人检测)

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)
  • ✅ 支持邮箱 + 密码身份验证
  • ✅ 支持魔法链接
另请参阅

已知问题预防

此技能可预防 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


官方文档


软件包版本(已于 2025-10-22 验证)

```json { "dependencies": { "@clerk/nextjs": "^6.33.3", "@clerk/clerk-react": "^5.51.0", "@clerk/backend": "^2.17.2" } } ```

评论

支持 Markdown