clerk-pack
Complete Clerk integration skill pack with 24 skills covering authentication, user management, embeddable UIs, and identity platform. Flagship tier vendor pack.
Installation
Open Claude Code and run this command:
/plugin install clerk-pack@claude-code-plugins-plus
Use --global to install for all projects, or --project for current project only.
What It Does
> 24 production-ready Claude Code skills for Clerk authentication -- real @clerk/nextjs v6 API code, not templates.
Skills (24) plugin-local skills
Configure Clerk CI/CD integration with GitHub Actions and testing.
Clerk CI Integration
Overview
Set up CI/CD pipelines with Clerk authentication testing. Covers GitHub Actions workflows, Playwright E2E tests with Clerk auth, test user management, and CI secrets configuration.
Prerequisites
- GitHub repository with Actions enabled
- Clerk test API keys (
pk_test_/sk_test_) - npm/pnpm project configured
Instructions
Step 1: GitHub Actions Workflow
# .github/workflows/test.yml
name: Test with Clerk Auth
on:
pull_request:
branches: [main]
push:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
env:
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY: ${{ secrets.CLERK_PK_TEST }}
CLERK_SECRET_KEY: ${{ secrets.CLERK_SK_TEST }}
CLERK_WEBHOOK_SECRET: ${{ secrets.CLERK_WEBHOOK_SECRET_TEST }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: npm
- run: npm ci
- run: npm run build
- run: npm test
- name: Install Playwright
run: npx playwright install --with-deps chromium
- name: Run E2E tests
run: npx playwright test
env:
CLERK_TEST_USER_EMAIL: ${{ secrets.CLERK_TEST_USER_EMAIL }}
CLERK_TEST_USER_PASSWORD: ${{ secrets.CLERK_TEST_USER_PASSWORD }}
Step 2: Configure GitHub Secrets
Add these secrets in GitHub repo > Settings > Secrets:
| Secret | Value |
|---|---|
CLERK_PK_TEST |
pk_test_... from dev instance |
CLERK_SK_TEST |
sk_test_... from dev instance |
CLERK_WEBHOOK_SECRET_TEST |
whsec_... from dev webhooks |
CLERK_TEST_USER_EMAIL |
ci-test@yourapp.com |
CLERK_TEST_USER_PASSWORD |
Strong test password |
Step 3: Playwright Auth Setup
// e2e/auth.setup.ts
import { test as setup, expect } from '@playwright/test'
import path from 'path'
const authFile = path.join(__dirname, '.auth/user.json')
setup('authenticate', async ({ page }) => {
await page.goto('/sign-in')
// Fill Clerk sign-in form
await page.fill('input[name="identifier"]', process.env.CLERK_TEST_USER_EMAIL!)
await page.click('button:has-text("Continue")')
await page.fill('input[name="password"]', process.env.CLERK_TEST_USER_PASSWORD!)
await page.click('button:has-text("Continue")')
// Wait for redirect to authenticated page
await page.waitForURL('/dashboard')
await expect(page.locator('text=Dashboard')).toBeVisibleTroubleshoot common Clerk errors and issues.
Clerk Common Errors
Overview
Diagnose and resolve common Clerk authentication errors. Organized by error category with root cause analysis and fix code.
Prerequisites
- Clerk SDK installed
- Access to Clerk Dashboard for configuration verification
- Browser developer tools for client-side debugging
Instructions
Error Category 1: Configuration Errors
Clerk: Missing publishableKey
// Cause: ClerkProvider not receiving key
// Fix: Ensure env var is set and accessible
// .env.local
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_test_...
// Verify in code:
console.log('PK:', process.env.NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY) // Should not be undefined
ClerkProvider must wrap your application
// Cause: Clerk hook used outside provider
// Fix: Wrap root layout with ClerkProvider
// app/layout.tsx
import { ClerkProvider } from '@clerk/nextjs'
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<ClerkProvider>
<html><body>{children}</body></html>
</ClerkProvider>
)
}
Error Category 2: Authentication Errors
form_identifier_not_found
// Cause: Email not registered in Clerk instance
// Fix: Check correct Clerk instance (dev vs prod) and user exists
// Diagnostic:
import { createClerkClient } from '@clerk/backend'
const clerk = createClerkClient({ secretKey: process.env.CLERK_SECRET_KEY! })
const users = await clerk.users.getUserList({ emailAddress: ['user@example.com'] })
console.log('User found:', users.totalCount > 0)
form_password_incorrect
// Cause: Wrong password or user set up with OAuth only
// Fix: Check auth strategy in Clerk Dashboard
// User may need password reset:
// Dashboard > Users > Select user > Authentication > Reset password
session_exists (Error during sign-in)
// Cause: User already has active session
// Fix: Sign out first or redirect
'use client'
import { useAuth } from '@clerk/nextjs'
import { redirect } from 'next/navigation'
export default function SignInPage() {
const { isSignedIn } = useAuth()
if (isSignedIn) redirect('/dashboard')
// ... render sign-in form
}
Error Category 3: Middleware Errors
Infinite redirect loop
// Cause: Sign-in page not in public routes
// Fix: Add auth pages to public route matcher
import { clerkMiddleware, createRouteMatcher } from '@clerk/nextjs/sImplement user sign-up and sign-in flows with Clerk.
Clerk Core Workflow A: Sign-Up & Sign-In
Overview
Implement authentication flows with Clerk: pre-built components for rapid setup, custom forms for full UI control, OAuth social login, email/phone verification, and multi-factor authentication.
Prerequisites
- Clerk SDK installed and configured (
clerk-install-authcompleted) - OAuth providers enabled in Dashboard > User & Authentication > Social Connections
- Sign-in/sign-up environment variables set
Instructions
Step 1: Pre-Built Components (Quick Start)
// app/sign-in/[[...sign-in]]/page.tsx
import { SignIn } from '@clerk/nextjs'
export default function SignInPage() {
return (
<div className="flex min-h-screen items-center justify-center">
<SignIn
appearance={{
elements: {
rootBox: 'mx-auto',
card: 'shadow-xl rounded-2xl',
headerTitle: 'text-2xl',
socialButtonsBlockButton: 'rounded-lg',
},
}}
routing="path"
path="/sign-in"
signUpUrl="/sign-up"
/>
</div>
)
}
// app/sign-up/[[...sign-up]]/page.tsx
import { SignUp } from '@clerk/nextjs'
export default function SignUpPage() {
return (
<div className="flex min-h-screen items-center justify-center">
<SignUp routing="path" path="/sign-up" signInUrl="/sign-in" />
</div>
)
}
# .env.local — routing configuration
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
Step 2: Custom Sign-In Form (Full UI Control)
'use client'
import { useSignIn } from '@clerk/nextjs'
import { useRouter } from 'next/navigation'
import { useState } from 'react'
export function CustomSignIn() {
const { signIn, setActive, isLoaded } = useSignIn()
const router = useRouter()
const [email, setEmail] = useState('')
const [password, setPassword] = useState('')
const [error, setError] = useState('')
const [loading, setLoading] = useState(false)
if (!isLoaded) return null
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault()
setError('')
setLoading(true)
try {
const result = await signIn.create({
identifier: email,
password,
})
if (result.status === 'complete') {
// Session created — activate it and redirect
await setActive({ session: result.createdSessionId })
router.push('/dashbImplement session management and middleware with Clerk.
Clerk Core Workflow B: Session & Middleware
Overview
Implement session management and route protection with Clerk middleware. Covers clerkMiddleware() configuration, auth() patterns, custom session claims, JWT templates for external services, organization-scoped sessions, and session token v2.
Use when managing user sessions, configuring route protection, or implementing token refresh and custom JWT templates.
Prerequisites
@clerk/nextjsinstalled with ClerkProvider wrapping the app- Next.js 14+ with App Router (or adapt patterns for your stack)
- Working publishable + secret Clerk keys in env
Instructions
- Confirm ClerkProvider and env keys are live (see
clerk-install-auth/clerk-hello-world). - Configure
clerkMiddleware()matcher / public routes for the routes you want open. - Use
auth()/currentUser()on protected pages and API routes; fail closed when unauthenticated. - For custom claims or external JWT consumers, configure session token templates carefully (size limits).
- For org-scoped apps, bind authorization to org id + role claims, not only user id.
- Verify: signed-out user cannot hit protected routes; signed-in user can; org role gates hold.
Deep patterns, JWT templates, and edge cases: session-middleware-deep-dive.md.
Output
- Session/middleware configuration enforcing the requested protection rules
- Documented JWT/session claim changes when customized
- Verification steps for protected vs public routes
Examples
Protect all routes except marketing
User: Protect everything except /, /pricing, and Clerk sign-in routes.
Skill: configures clerkMiddleware publicRoutes / matcher and verifies unauth redirect.
Custom session claim for plan tier
User: Put plan_tier on the session JWT for feature flags.
Skill: configures session token template and validates claim size + read path.
Error Handling
| Condition | Response |
|---|---|
| Route is accidentally public | Fail the verification gate and narrow the matcher before release. |
| Session claim is absent or stale | Treat the request as unauthorized for the dependent feature and refresh through the supported path. |
| JWT exceeds consumer limits | Remove nonessential claims; retrieve server-side attributes through an authorized backend. |
| Organization context is missing | Deny the org-scoped operation and require explicit selection/role validation. |
Resources
-
clerk-cost-tuning View full skill →
Optimize Clerk costs and understand pricing.
ReadWriteEditGrepClerk Cost Tuning
Overview
Understand Clerk pricing and optimize costs. Clerk charges by Monthly Retained Users (MRU) — a user is counted as retained only when they return 24+ hours after signing up. Covers pricing tiers, MRU reduction strategies, caching to reduce API calls, and usage monitoring.
Prerequisites
- Clerk account active
- Understanding of MRU (Monthly Retained Users)
- Application usage patterns known
Instructions
Step 1: Understand Clerk Pricing Model
Plan Price MRU Included Extra MRU Free (Hobby) $0/mo 50,000 MRU N/A Pro $25/mo ($20/mo billed annually) 50,000 MRU $0.02/MRU Business $300/mo ($250/mo billed annually) 50,000 MRU $0.02/MRU Enterprise Custom Custom Custom Key pricing concepts:
- MRU = unique user who returns to your app 24+ hours after signing up ("first day free" — sign-up-day activity never counts)
- Users who sign up and never return are not billed
- Users who only visit public pages are not counted
- Bot/crawler sessions are not counted
- Test/development instances are free and unlimited
Step 2: Reduce MRU Count
// Strategy 1: Defer authentication — don't force sign-in until necessary // middleware.ts import { clerkMiddleware, createRouteMatcher } from '@clerk/nextjs/server' const requiresAuth = createRouteMatcher([ '/dashboard(.*)', '/settings(.*)', '/api/protected(.*)', ]) export default clerkMiddleware(async (auth, req) => { // Only require auth for specific routes (not entire site) if (requiresAuth(req)) { await auth.protect() } })// Strategy 2: Use anonymous access for read-only features // app/blog/[slug]/page.tsx import { auth } from '@clerk/nextjs/server' export default async function BlogPost({ params }: { params: { slug: string } }) { const { userId } = await auth() // Check but don't require const post = await db.post.findUnique({ where: { slug: params.slug } }) return ( <article> <h1>{post?.title}</h1> <div>{post?.content}</div> {userId ? <CommentForm /> : <p>Sign in to comment</p>} </article> ) }Step 3: Cache to Reduce API Calls
// lib/user-cache.ts import { cache } from 'react' import { currentUser } from '@clerk/nextjs/server' // Deduplicate within single request (free) export const getUser = cache(async () => { return currentUser() })
Handle user data, privacy, and GDPR compliance with Clerk.
Clerk Data Handling
Overview
Manage user data, implement privacy features, and ensure GDPR/CCPA compliance using the Clerk Backend API. Covers data export, right to be forgotten, consent management, and audit logging.
Prerequisites
- Clerk integration working
- Understanding of GDPR/CCPA requirements
- Database with user-related data linked by Clerk user IDs
Instructions
Step 1: User Data Export
// app/api/privacy/export/route.ts
import { auth, clerkClient } from '@clerk/nextjs/server'
export async function GET() {
const { userId } = await auth()
if (!userId) return Response.json({ error: 'Unauthorized' }, { status: 401 })
const client = await clerkClient()
const clerkUser = await client.users.getUser(userId)
// Gather data from Clerk
const clerkData = {
id: clerkUser.id,
emails: clerkUser.emailAddresses.map((e) => e.emailAddress),
firstName: clerkUser.firstName,
lastName: clerkUser.lastName,
createdAt: clerkUser.createdAt,
lastSignInAt: clerkUser.lastSignInAt,
publicMetadata: clerkUser.publicMetadata,
}
// Gather data from your database
const appData = await db.user.findUnique({
where: { clerkId: userId },
include: { posts: true, comments: true, preferences: true },
})
return Response.json({
exportDate: new Date().toISOString(),
clerkProfile: clerkData,
applicationData: appData,
})
}
Step 2: User Deletion (Right to be Forgotten)
// app/api/privacy/delete/route.ts
import { auth, clerkClient } from '@clerk/nextjs/server'
export async function DELETE() {
const { userId } = await auth()
if (!userId) return Response.json({ error: 'Unauthorized' }, { status: 401 })
const deletionLog: { step: string; status: string }[] = []
try {
// 1. Delete application data first
await db.comment.deleteMany({ where: { authorId: userId } })
deletionLog.push({ step: 'comments', status: 'deleted' })
await db.post.deleteMany({ where: { authorId: userId } })
deletionLog.push({ step: 'posts', status: 'deleted' })
await db.user.delete({ where: { clerkId: userId } })
deletionLog.push({ step: 'app_user', status: 'deleted' })
// 2. Delete from Clerk (this ends the session)
const client = await clerkClient()
await client.users.deleteUser(userId)
deletionLog.push({ step: 'clerk_user', status: 'deleted' })
// 3. Log deletion for compliance audit trail
await db.auditLog.create({
data: {
action: 'USER_DELETED',
subjectId: userId,
details: JSON.stringify(deletionLog),
timestamp: new Date(),
},
})
return Response.json({ deleted: true, log: deletionLog })
} catch (error) {
return Response.json({ error: 'Collect comprehensive debug information for Clerk issues.
Clerk Debug Bundle
Current State
!node --version 2>/dev/null || echo 'N/A' !npm list @clerk/nextjs @clerk/clerk-react @clerk/express 2>/dev/null | grep clerk || echo 'No Clerk packages found'
Overview
Collect all necessary debug information for Clerk troubleshooting and support tickets. Generates an environment report, runtime health check, client-side debug panel, and support bundle.
Prerequisites
- Clerk SDK installed
- Access to application logs
- Browser with developer tools
Instructions
Step 1: Environment Debug Script
// scripts/clerk-debug.ts
import { createClerkClient } from '@clerk/backend'
async function collectDebugInfo() {
const info: Record<string, any> = {
timestamp: new Date().toISOString(),
nodeVersion: process.version,
platform: process.platform,
env: {
hasPK: !!process.env.NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY,
hasSK: !!process.env.CLERK_SECRET_KEY,
pkPrefix: process.env.NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY?.slice(0, 8) + '...',
nodeEnv: process.env.NODE_ENV,
},
}
// Test API connectivity
try {
const clerk = createClerkClient({ secretKey: process.env.CLERK_SECRET_KEY! })
const users = await clerk.users.getUserList({ limit: 1 })
info.apiConnectivity = { status: 'ok', userCount: users.totalCount }
} catch (err: any) {
info.apiConnectivity = { status: 'error', message: err.message, code: err.status }
}
// Check package versions
try {
const pkg = require('./package.json')
info.packages = Object.entries(pkg.dependencies || {})
.filter(([k]) => k.includes('clerk'))
.reduce((acc, [k, v]) => ({ ...acc, [k]: v }), {})
} catch {
info.packages = 'Could not read package.json'
}
console.log(JSON.stringify(info, null, 2))
return info
}
collectDebugInfo()
Run with:
npx tsx scripts/clerk-debug.ts
Step 2: Runtime Health Check Endpoint
// app/api/clerk-health/route.ts
import { auth, clerkClient } from '@clerk/nextjs/server'
export async function GET() {
const checks: Record<string, { status: string; detail?: string }> = {}
// Check 1: SDK loaded
checks.sdk = { status: 'ok', detail: 'Clerk SDK loaded' }
// Check 2: Auth function works
try {
const { userId } = await auth()
checks.auth = { status: 'ok', detail: userId ? `Authenticated as ${userId}` : 'Not authenticated (expected for health check)' }
} catch (err: any) {
checks.auth = { status: 'error', detail: err.message }
}
// Check 3: Backend API connectivity
try {
const client = await clerkClient()
await client.users.getUserList({ limit: 1 })
Configure Clerk for deployment on various platforms.
Clerk Deploy Integration
Overview
Deploy Clerk-authenticated applications to Vercel, Netlify, Railway, and other hosting platforms. Covers environment variable configuration, domain setup, and webhook endpoint configuration.
Prerequisites
- Clerk production instance with
pk_live_/sk_live_keys - Production domain configured
- Hosting platform account
Instructions
Step 1: Vercel Deployment
# Add Clerk env vars to Vercel
vercel env add NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY production
# Paste: pk_live_...
vercel env add CLERK_SECRET_KEY production
# Paste: sk_live_...
vercel env add CLERK_WEBHOOK_SECRET production
# Paste: whsec_...
# Add for preview deployments too
vercel env add NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY preview
vercel env add CLERK_SECRET_KEY preview
Configure Clerk Dashboard for Vercel:
- Go to Dashboard > Domains and add your production domain
- Set Home URL:
https://myapp.com - Set Sign-in URL:
https://myapp.com/sign-in - Set Sign-up URL:
https://myapp.com/sign-up - Set After sign-in URL:
https://myapp.com/dashboard
Step 2: Netlify Deployment
# Add env vars via Netlify CLI
netlify env:set NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY pk_live_...
netlify env:set CLERK_SECRET_KEY sk_live_...
For Netlify Functions (serverless API routes):
// netlify/functions/clerk-auth.ts
import { createClerkClient } from '@clerk/backend'
const clerk = createClerkClient({ secretKey: process.env.CLERK_SECRET_KEY! })
export async function handler(event: any) {
const token = event.headers.authorization?.replace('Bearer ', '')
if (!token) {
return { statusCode: 401, body: JSON.stringify({ error: 'No token' }) }
}
try {
const session = await clerk.sessions.verifySession(token, token)
return {
statusCode: 200,
body: JSON.stringify({ userId: session.userId }),
}
} catch {
return { statusCode: 401, body: JSON.stringify({ error: 'Invalid token' }) }
}
}
Step 3: Railway Deployment
# Set env vars via Railway CLI
railway variables set NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_live_...
railway variables set CLERK_SECRET_KEY=sk_live_...
railway variables set CLERK_WEBHOOK_SECRET=whsec_...
Step 4: Docker Deployment
# Dockerfile
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
# Build-time env vars (public key only)
ARG NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY
ENV NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=$NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY
RConfigure enterprise SSO, role-based access control, and organization management.
Clerk Enterprise RBAC
Overview
Implement enterprise-grade role-based access control, organization management, and SSO with Clerk. Covers custom roles and permissions, organization lifecycle, multi-tenant access patterns, SAML/OIDC SSO, and the Backend API for programmatic role management (released Nov 2025).
Prerequisites
- Clerk Pro or Enterprise plan (Organizations + SSO require paid plan)
- Organizations feature enabled in Clerk Dashboard > Organizations > Settings
- Next.js 14+ with App Router (examples use
@clerk/nextjs)
Instructions
Step 1: Enable Organizations and Add UI Components
// app/org-selector/page.tsx
import { OrganizationSwitcher, OrganizationProfile } from '@clerk/nextjs'
export default function OrgPage() {
return (
<div className="p-8">
<h1>Select Organization</h1>
<OrganizationSwitcher
hidePersonal={false}
afterSelectOrganizationUrl="/dashboard"
afterCreateOrganizationUrl="/dashboard"
/>
<div className="mt-8">
<OrganizationProfile />
</div>
</div>
)
}
Step 2: Define Custom Roles and Permissions
Configure in Clerk Dashboard > Organizations > Roles and Permissions.
Default roles (built-in):
| Role | Key | Built-in Permissions |
|---|---|---|
| Admin | org:admin |
Full org management (members, settings, billing) |
| Member | org:member |
View org, read-only access |
Custom permissions (create in Dashboard > Organizations > Permissions):
| Permission | Key | Description |
|---|---|---|
| Read data | org:data:read |
View organization resources |
| Write data | org:data:write |
Create/update resources |
| Delete data | org:data:delete |
Delete resources |
| Manage billing | org:billing:manage |
Access billing settings |
| View analytics | org:analytics:read |
Access analytics dashboard |
Custom roles (create in Dashboard > Organizations > Roles):
| Role | Permissions | Use Case | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
org:manager |
data:read, data:write, analytics:read |
Content managers | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
org:viewer |
data:read |
<
| Category | Symptoms | Severity |
|---|---|---|
| Clerk outage | status.clerk.com shows incident, all auth fails | Critical |
| Key compromise | Unauthorized access detected | Critical |
| Middleware failure | All routes return 500 | High |
| Session issues | Users randomly logged out | Medium |
| Webhook backlog | User sync falling behind | Low |
Quick diagnostic:
#!/bin/bash
# scripts/clerk-triage.sh
set -euo pipefail
echo "=== Clerk Incident Triage ==="
echo "Time: $(date -u +%Y-%m-%dT%H:%M:%SZ)"
# 1. Check Clerk status
echo -e "\n--- Clerk Status ---"
curl -s https://status.clerk.com/api/v2/status.json | python3 -c "
import json,sys
d=json.load(sys.stdin)
print(f\"Status: {d['status']['description']}\")" 2>/dev/null || echo "Cannot reach status API"
# 2. Check API connectivity
echo -e "\n--- API Connectivity ---"
HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" -H "Authorization: Bearer ${CLERK_SECRET_KEY}" \
https://api.clerk.com/v1/users?limit=1 2>/dev/null)
echo "API response: HTTP $HTTP_CODE"
# 3. Check app health
echo -e "\n--- App Health ---"
curl -s http://localhost:3000/api/clerk-health 2>/dev/null | python3 -m json.tool || echo "App not reachable"
Step 2: Emergency Auth Bypass (Clerk Outage Only)
// middleware.ts — emergency bypass mode
import { clerkMiddleware, createRouteMatcher } from '@clerk/nextjs/server'
import { NextResponse } from 'next/server'
const EMERGENCY_BYPASS = process.env.CLERK_EMERGENCY_BYPASS === 'true'
const isPublicRoute = createRouteMatcher(['/', '/sign-in(.*)', '/sign-up(.*)'])
export default clerkMiddleware(async (auth, req) => {
// Emergency bypass: allow all requests when Clerk is down
if (EMERGENCY_BYPASS) {
console.warn('[EMERGENCY] Auth bypass active — all requests allowed')
const response = NextResponse.next()
response.headers.set('X-Auth-Bypass', 'true')
return response
}
if (!isPublicRoute(req)) {
await auth.protect()
}
})
A
Install and configure Clerk SDK/CLI authentication.
Clerk Install & Auth
Overview
Set up Clerk SDK and configure authentication for Next.js, React, or Express. This skill covers SDK installation, environment variables, ClerkProvider, middleware, and initial auth verification.
Prerequisites
- Node.js 18+
- Package manager (npm, pnpm, or yarn)
- Clerk account at dashboard.clerk.com
- Publishable Key (
pk_test_*) and Secret Key (sk_test_*) from Clerk Dashboard > API Keys
Instructions
Step 1: Install SDK for Your Framework
set -euo pipefail
# Next.js (App Router or Pages Router)
npm install @clerk/nextjs
# React SPA (Vite, CRA, etc.)
npm install @clerk/clerk-react
# Express / Node.js backend
npm install @clerk/express
# Backend-only (Cloudflare Workers, serverless, etc.)
npm install @clerk/backend
Step 2: Configure Environment Variables
# .env.local — never commit this file
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_test_...
CLERK_SECRET_KEY=sk_test_...
# Optional: routing URLs
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
Ensure .env.local is in .gitignore:
echo ".env.local" >> .gitignore
Step 3: Add ClerkProvider (Next.js App Router)
// app/layout.tsx
import { ClerkProvider, SignInButton, SignedIn, SignedOut, UserButton } from '@clerk/nextjs'
import './globals.css'
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<ClerkProvider>
<html lang="en">
<body>
<header className="flex justify-between p-4">
<SignedOut>
<SignInButton />
</SignedOut>
<SignedIn>
<UserButton />
</SignedIn>
</header>
{children}
</body>
</html>
</ClerkProvider>
)
}
Step 4: Add Middleware
// middleware.ts (project root, NOT inside app/ or src/app/)
import { clerkMiddleware, createRouteMatcher } from '@clerk/nextjs/server'
// Define which routes should be publicly accessible
const isPublicRoute = createRouteMatcher([
'/',
'/sign-in(.*)',
'/sign-up(.*)',
'/api/webhooks(.*)',
])
export default clerkMiddleware(async (auth, req) => {
if (!isPublicRoute(req)) {
await auth.protect()
}
})
export const config = {
matcher: [
// Skip Next.js internals and static files
'/((?!_next|[^?]*\\.(?:html?|css|js(?!on)|jpe?g|webp|png|gif|svg|ttf|wofSet up local development workflow with Clerk.
Clerk Local Dev Loop
Overview
Configure an efficient local development workflow with Clerk authentication, including test users, hot reload, and mock auth for unit tests.
Prerequisites
- Clerk SDK installed (
clerk-install-authcompleted) - Development instance created in Clerk Dashboard
- Node.js development environment
Instructions
Step 1: Configure Development Instance
Create a separate Clerk development instance to isolate test data from production.
# .env.local — use test keys (pk_test_ / sk_test_)
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_test_...
CLERK_SECRET_KEY=sk_test_...
# Optional: Enable Clerk debug logging
CLERK_DEBUG=true
Clerk development instances provide:
- No email verification required
- Test phone numbers accepted
- Relaxed rate limits
- OAuth with test credentials
Step 2: Set Up Test Users
// scripts/seed-test-users.ts
import { createClerkClient } from '@clerk/backend'
const clerk = createClerkClient({ secretKey: process.env.CLERK_SECRET_KEY! })
async function seedTestUsers() {
const users = [
{ emailAddress: ['admin@test.com'], password: 'test1234', firstName: 'Admin', lastName: 'User' },
{ emailAddress: ['member@test.com'], password: 'test1234', firstName: 'Member', lastName: 'User' },
]
for (const user of users) {
try {
await clerk.users.createUser(user)
console.log(`Created: ${user.emailAddress[0]}`)
} catch (err: any) {
if (err.status === 422) {
console.log(`Already exists: ${user.emailAddress[0]}`)
} else {
throw err
}
}
}
}
seedTestUsers()
Run with:
npx tsx scripts/seed-test-users.ts
Step 3: Configure Hot Reload
// next.config.ts — ensure Clerk works with hot reload
const nextConfig = {
// Clerk SDK is compatible with Turbopack
experimental: {
// turbo: {}, // Uncomment if using Turbopack
},
// Prevent Clerk SDK from being bundled incorrectly
serverExternalPackages: ['@clerk/backend'],
}
export default nextConfig
# Start dev server with HTTPS (required for some Clerk features)
npx next dev --experimental-https
Step 4: Development Scripts
// package.json scripts
{
"scripts": {
"dev": "next dev",
"dev:https": "next dev --experimental-https",
"dev:seed": "tsx scripts/seed-test-users.ts",
"dev:tunnel": "ngrok http 3000",
"dev:webhook": "ngrok http 3000 --log stdout"
}
}
Step 5
Migrate from other authentication providers to Clerk.
Clerk Migration Deep Dive
Current State
!npm list @auth0/nextjs-auth0 next-auth @supabase/auth-helpers-nextjs firebase 2>/dev/null | grep -E "auth0|next-auth|supabase|firebase" || echo 'No auth providers detected'
Overview
Comprehensive guide to migrating from Auth0, Firebase Auth, Supabase Auth, or NextAuth to Clerk. Covers user data export, bulk import, parallel running, and phased migration.
Prerequisites
- Current auth provider access with admin/export permissions
- Clerk account with API keys
- Git repository with clean working state
- Migration timeline planned (recommend 2-4 weeks)
Instructions
Step 1: Export Users from Current Provider
Auth0 Export:
# Export users via Auth0 Management API
curl -s -H "Authorization: Bearer $AUTH0_TOKEN" \
"https://$AUTH0_DOMAIN/api/v2/users?per_page=100&page=0" \
| jq '[.[] | {email: .email, name: .name, picture: .picture, created_at: .created_at}]' \
> auth0-users.json
NextAuth (Prisma) Export:
// scripts/export-nextauth-users.ts
const users = await prisma.user.findMany({
include: { accounts: true },
})
const exported = users.map((u) => ({
email: u.email,
name: u.name,
image: u.image,
provider: u.accounts[0]?.provider,
createdAt: u.createdAt,
}))
await fs.writeFile('nextauth-users.json', JSON.stringify(exported, null, 2))
Step 2: Import Users to Clerk
// scripts/import-to-clerk.ts
import { createClerkClient } from '@clerk/backend'
import users from './auth0-users.json'
const clerk = createClerkClient({ secretKey: process.env.CLERK_SECRET_KEY! })
interface MigrationResult {
email: string
status: 'created' | 'exists' | 'error'
clerkId?: string
error?: string
}
async function importUsers(): Promise<MigrationResult[]> {
const results: MigrationResult[] = []
for (const user of users) {
try {
const created = await clerk.users.createUser({
emailAddress: [user.email],
firstName: user.name?.split(' ')[0],
lastName: user.name?.split(' ').slice(1).join(' '),
skipPasswordRequirement: true, // User will set password on first sign-in
})
results.push({ email: user.email, status: 'created', clerkId: created.id })
console.log(`Created: ${user.email} -> ${created.id}`)
} catch (err: any) {
if (err.status === 422) {
results.push({ email: user.email, status: 'exists' })
} else {
results.push({ email: user.email, status: 'error', error: err.message })
}
}
// Respect rate limits
await new Promise((resolve) => setTimeout(reConfigure Clerk for multiple environments (dev, staging, production).
Clerk Multi-Environment Setup
Overview
Configure Clerk across development, staging, and production environments with separate instances, environment-aware configuration, and safe promotion workflows.
Prerequisites
- Clerk account (one instance per environment recommended)
- CI/CD pipeline (GitHub Actions, Vercel, etc.)
- Environment variable management in place
Instructions
Step 1: Create Clerk Instances
Create separate Clerk instances in the Dashboard for each environment:
| Environment | Instance | Key Prefix | Domain |
|---|---|---|---|
| Development | my-app-dev | pk_test_ / sk_test_ |
localhost:3000 |
| Staging | my-app-staging | pk_test_ / sk_test_ |
staging.myapp.com |
| Production | my-app-prod | pk_live_ / sk_live_ |
myapp.com |
Step 2: Environment Configuration Files
# .env.local (development - git-ignored)
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_test_dev...
CLERK_SECRET_KEY=sk_test_dev...
CLERK_WEBHOOK_SECRET=whsec_dev...
# .env.staging (staging)
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_test_staging...
CLERK_SECRET_KEY=sk_test_staging...
CLERK_WEBHOOK_SECRET=whsec_staging...
# .env.production (production)
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_live_prod...
CLERK_SECRET_KEY=sk_live_prod...
CLERK_WEBHOOK_SECRET=whsec_prod...
Step 3: Environment-Aware Configuration
// lib/clerk-config.ts
type ClerkEnv = 'development' | 'staging' | 'production'
function getClerkEnv(): ClerkEnv {
const key = process.env.NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY || ''
if (key.startsWith('pk_live_')) return 'production'
if (process.env.VERCEL_ENV === 'preview') return 'staging'
return 'development'
}
export const clerkConfig = {
env: getClerkEnv(),
get isDev() { return this.env === 'development' },
get isProd() { return this.env === 'production' },
signInUrl: '/sign-in',
signUpUrl: '/sign-up',
afterSignInUrl: '/dashboard',
get allowedRedirectOrigins() {
switch (this.env) {
case 'production': return ['https://myapp.com']
case 'staging': return ['https://staging.myapp.com']
default: return ['http://localhost:3000']
}
},
}
Step 4: Startup Validation
// lib/validate-env.ts
export function validateClerkEnv() {
const required = [
'NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY',
'CLERK_SECRET_KEY',
]
const missing = required.filter((key) => !process.env[key])
if (miImplement monitoring, logging, and observability for Clerk authentication.
Clerk Observability
Overview
Implement monitoring, logging, and observability for Clerk authentication. Covers structured auth logging, middleware performance tracking, webhook event monitoring, Sentry integration, and health check endpoints.
Prerequisites
- Clerk integration working
- Monitoring platform (Sentry, DataDog, or Pino logger at minimum)
- Logging infrastructure (structured JSON logs recommended)
Instructions
Step 1: Structured Authentication Event Logging
// lib/auth-logger.ts
import pino from 'pino'
const logger = pino({
level: process.env.LOG_LEVEL || 'info',
transport: process.env.NODE_ENV === 'development' ? { target: 'pino-pretty' } : undefined,
})
export function logAuthEvent(event: {
type: 'sign_in' | 'sign_out' | 'sign_up' | 'permission_denied' | 'session_expired'
userId?: string | null
orgId?: string | null
path: string
metadata?: Record<string, any>
}) {
logger.info({
category: 'auth',
...event,
timestamp: new Date().toISOString(),
})
}
export function logAuthError(error: Error, context: { userId?: string; path: string }) {
logger.error({
category: 'auth',
error: error.message,
stack: error.stack,
...context,
timestamp: new Date().toISOString(),
})
}
Step 2: Middleware Performance Monitoring
// middleware.ts
import { clerkMiddleware, createRouteMatcher } from '@clerk/nextjs/server'
const isPublicRoute = createRouteMatcher(['/', '/sign-in(.*)', '/sign-up(.*)'])
export default clerkMiddleware(async (auth, req) => {
const start = Date.now()
if (!isPublicRoute(req)) {
await auth.protect()
}
const duration = Date.now() - start
const { userId } = await auth()
// Log slow auth checks
if (duration > 100) {
console.warn(`[Auth Perf] ${req.nextUrl.pathname} took ${duration}ms`, {
userId: userId || 'anonymous',
method: req.method,
})
}
// Add timing header for debugging
const response = new Response(null, { status: 200 })
response.headers.set('X-Auth-Duration', `${duration}ms`)
})
Step 3: Webhook Event Tracking
// app/api/webhooks/clerk/route.ts
import { logAuthEvent } from '@/lib/auth-logger'
async function handleWebhookEvent(evt: WebhookEvent) {
const startTime = Date.now()
// Track webhook processing metrics
const metrics = {
eventType: evt.type,
receivedAt: new Date().toISOString(),
processingTimeMs: 0,
}
switch (evt.type) {
case 'user.created':
logAuthEvent({
type: 'sign_up',
userId: evt.data.id,
path: '/webhooks/clerk',
metadata: { email: evt.data.eOptimize Clerk authentication performance.
Clerk Performance Tuning
Overview
Optimize Clerk authentication for best performance. Covers middleware optimization, user data caching, token handling, lazy loading, and edge runtime configuration.
Prerequisites
- Clerk integration working
- Performance monitoring in place (Lighthouse, Web Vitals)
- Understanding of Next.js rendering strategies
Instructions
Step 1: Optimize Middleware (Skip Static Assets)
// middleware.ts — avoid running auth on static files
import { clerkMiddleware, createRouteMatcher } from '@clerk/nextjs/server'
const isPublicRoute = createRouteMatcher(['/', '/sign-in(.*)', '/sign-up(.*)', '/api/webhooks(.*)'])
export default clerkMiddleware(async (auth, req) => {
if (!isPublicRoute(req)) {
await auth.protect()
}
})
// Restrict matcher to avoid processing static assets
export const config = {
matcher: [
// Skip _next, static files, and images
'/((?!_next/static|_next/image|favicon.ico|.*\\.(?:svg|png|jpg|jpeg|gif|webp|ico)).*)',
'/(api|trpc)(.*)',
],
}
Step 2: Cache User Data
// lib/cached-user.ts
import { auth, currentUser } from '@clerk/nextjs/server'
import { cache } from 'react'
// React cache: deduplicates within a single request
export const getAuthUser = cache(async () => {
const { userId } = await auth()
if (!userId) return null
return currentUser()
})
// Usage in multiple server components (only one Clerk API call per request):
// const user = await getAuthUser()
For cross-request caching with unstable_cache:
import { unstable_cache } from 'next/cache'
import { clerkClient } from '@clerk/nextjs/server'
export const getCachedUserProfile = unstable_cache(
async (userId: string) => {
const client = await clerkClient()
const user = await client.users.getUser(userId)
return {
id: user.id,
name: `${user.firstName} ${user.lastName}`,
email: user.emailAddresses[0]?.emailAddress,
imageUrl: user.imageUrl,
}
},
['user-profile'],
{ revalidate: 300 } // Cache for 5 minutes
)
Step 3: Optimize Token Handling
// lib/token-cache.ts
let tokenCache: { token: string; expiresAt: number } | null = null
export async function getOptimizedToken(getToken: () => Promise<string | null>) {
// Reuse token if it has more than 30 seconds remaining
if (tokenCache && tokenCache.expiresAt > Date.now() + 30_000) {
return tokenCache.token
}
const token = await getToken()
if (token) {
const payload = JSON.parse(atob(token.split('.')[1]))
tokenCache = { token, expiresAt: payload.exp * 1000 }
}
return token
}
Production readiness checklist for Clerk deployment.
Clerk Production Checklist
Overview
Complete checklist to ensure your Clerk integration is production-ready. Covers environment config, security hardening, monitoring, error handling, and compliance.
Prerequisites
- Clerk integration working in development
- Production environment and domain configured
- CI/CD pipeline ready
Instructions
Step 1: Environment Configuration Checklist
| Check | Status | Action |
|---|---|---|
Using pk_live_ keys |
[ ] | Switch from test to live keys |
CLERK_SECRET_KEY is sk_live_ |
[ ] | Never use test keys in production |
.env.local in .gitignore |
[ ] | Prevent accidental secret commits |
CLERK_WEBHOOK_SECRET set |
[ ] | Required for webhook verification |
| Production domain in Clerk Dashboard | [ ] | Dashboard > Domains |
| Sign-in/sign-up URLs configured | [ ] | Set NEXT_PUBLIC_CLERK_SIGN_IN_URL etc. |
Step 2: Validation Script
// scripts/prod-readiness.ts
import { createClerkClient } from '@clerk/backend'
async function validateProduction() {
const checks: { name: string; pass: boolean; detail: string }[] = []
// 1. Live keys check
const pk = process.env.NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY || ''
const sk = process.env.CLERK_SECRET_KEY || ''
checks.push({
name: 'Live publishable key',
pass: pk.startsWith('pk_live_'),
detail: pk.startsWith('pk_live_') ? 'Using live key' : `Using ${pk.slice(0, 8)}... (should be pk_live_)`,
})
checks.push({
name: 'Live secret key',
pass: sk.startsWith('sk_live_'),
detail: sk.startsWith('sk_live_') ? 'Using live key' : 'Should be sk_live_ for production',
})
// 2. API connectivity
try {
const clerk = createClerkClient({ secretKey: sk })
await clerk.users.getUserList({ limit: 1 })
checks.push({ name: 'API connectivity', pass: true, detail: 'Backend API reachable' })
} catch (err: any) {
checks.push({ name: 'API connectivity', pass: false, detail: err.message })
}
// 3. Webhook secret
checks.push({
name: 'Webhook secret configured',
pass: !!process.env.CLERK_WEBHOOK_SECRET,
detail: process.env.CLERK_WEBHOOK_SECRET ? 'Set' : 'CLERK_WEBHOOK_SECRET missing',
})
// 4. Middleware exists
const fs = await import('fs')
const hasMiddleware = fs.existsSync('middleware.ts') || fs.existsSync('src/middleware.ts')
checks.push({
name: 'Middleware present',
pass: haUnderstand and manage Clerk rate limits and quotas.
Clerk Rate Limits
Overview
Understand Clerk's rate limiting system and implement strategies to avoid hitting limits. Covers Backend API rate limits, retry logic, batching, caching, and monitoring.
Prerequisites
- Clerk account with API access
- Understanding of your application's traffic patterns
- Monitoring/logging infrastructure
Instructions
Step 1: Understand Rate Limits
Clerk Backend API enforces rate limits per API key:
| Plan | Rate Limit | Burst |
|---|---|---|
| Free | 20 req/10s | 40 |
| Pro | 100 req/10s | 200 |
| Enterprise | Custom | Custom |
Rate limit headers returned on every response:
X-RateLimit-Limit— max requests per windowX-RateLimit-Remaining— remaining requestsX-RateLimit-Reset— seconds until window resets
Step 2: Implement Rate Limit Handling with Retry
// lib/clerk-api.ts
import { createClerkClient } from '@clerk/backend'
const clerk = createClerkClient({ secretKey: process.env.CLERK_SECRET_KEY! })
async function withRetry<T>(fn: () => Promise<T>, maxRetries = 3): Promise<T> {
for (let attempt = 0; attempt <= maxRetries; attempt++) {
try {
return await fn()
} catch (err: any) {
if (err.status === 429 && attempt < maxRetries) {
// Parse retry-after header or use exponential backoff
const retryAfter = err.headers?.['retry-after']
const waitMs = retryAfter ? parseInt(retryAfter) * 1000 : Math.pow(2, attempt) * 1000
console.warn(`Rate limited. Retrying in ${waitMs}ms (attempt ${attempt + 1}/${maxRetries})`)
await new Promise((resolve) => setTimeout(resolve, waitMs))
continue
}
throw err
}
}
throw new Error('Max retries exceeded')
}
// Usage
export async function getUser(userId: string) {
return withRetry(() => clerk.users.getUser(userId))
}
Step 3: Batch Operations
// lib/clerk-batch.ts
import { createClerkClient } from '@clerk/backend'
const clerk = createClerkClient({ secretKey: process.env.CLERK_SECRET_KEY! })
async function batchGetUsers(userIds: string[], batchSize = 10) {
const results = []
for (let i = 0; i < userIds.length; i += batchSize) {
const batch = userIds.slice(i, i + batchSize)
const users = await Promise.all(batch.map((id) => clerk.users.getUser(id)))
results.push(...users)
// Respect rate limits between batches
if (i + batchSize < userIds.length) {
await new Promise((resolve) => setTimeout(resolve, 500))
}
}
return results
}
// For listing: use paginatioReference architecture patterns for Clerk authentication.
Clerk Reference Architecture
Overview
Reference architectures for implementing Clerk in common application patterns: Next.js full-stack, microservices with shared auth, multi-tenant SaaS, and mobile + web with shared backend.
Prerequisites
- Understanding of web application architecture
- Familiarity with authentication patterns (JWT, sessions, OAuth)
- Knowledge of your tech stack and scaling requirements
Instructions
Architecture 1: Next.js Full-Stack Application
Browser
│
├─▸ Next.js Middleware (clerkMiddleware)
│ └─▸ Validates session token on every request
│
├─▸ Server Components (auth(), currentUser())
│ └─▸ Direct access to user data, no network call
│
├─▸ Client Components (useUser(), useAuth())
│ └─▸ Real-time auth state via ClerkProvider
│
├─▸ API Routes (auth() for userId, getToken() for JWT)
│ └─▸ Call external services with Clerk JWT
│
└─▸ Webhooks (/api/webhooks/clerk)
└─▸ Sync user data to database
// app/layout.tsx — entry point
import { ClerkProvider } from '@clerk/nextjs'
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<ClerkProvider>
<html><body>{children}</body></html>
</ClerkProvider>
)
}
// middleware.ts — auth boundary
import { clerkMiddleware, createRouteMatcher } from '@clerk/nextjs/server'
const isPublic = createRouteMatcher(['/', '/sign-in(.*)', '/sign-up(.*)', '/api/webhooks(.*)'])
export default clerkMiddleware(async (auth, req) => {
if (!isPublic(req)) await auth.protect()
})
Architecture 2: Microservices with Shared Auth
Browser ─▸ API Gateway / BFF (Next.js + Clerk)
│
├─▸ Service A (Node.js) ──── verifies JWT
├─▸ Service B (Python) ──── verifies JWT
└─▸ Service C (Go) ──────── verifies JWT
// BFF: Generate service-specific JWT
// app/api/proxy/[service]/route.ts
import { auth } from '@clerk/nextjs/server'
export async function GET(req: Request, { params }: { params: { service: string } }) {
const { userId, getToken } = await auth()
if (!userId) return Response.json({ error: 'Unauthorized' }, { status: 401 })
// Get JWT with service-specific claims
const token = await getToken({ template: params.service })
const serviceUrls: Record<string, string> = {
billing: process.env.BILLING_SERVICE_URL!,
analytics: process.env.ANALYTICS_SERVICE_URL!,
notifications: process.env.NOTIFICATION_SERVICE_URL!,
}
const response = await fetch(`${serviceUrls[params.service]}/api/data`, {
headers: { Authorization: `Bearer ${token}` },
})
return RespCommon Clerk SDK patterns and best practices.
Clerk SDK Patterns
Overview
Common patterns and best practices for using the Clerk SDK effectively across server components, client components, API routes, and middleware.
Prerequisites
- Clerk SDK installed and configured
- Basic understanding of React/Next.js
- ClerkProvider wrapping application
Instructions
Pattern 1: Server-Side Authentication
// Server Component — use auth() for lightweight checks
import { auth } from '@clerk/nextjs/server'
export default async function ServerPage() {
const { userId, orgId, has } = await auth()
if (!userId) return <div>Not authenticated</div>
if (!has({ permission: 'org:posts:create' })) return <div>No permission</div>
return <div>Authorized content for {userId}</div>
}
// Use currentUser() when you need full user profile data
import { currentUser } from '@clerk/nextjs/server'
export default async function ProfilePage() {
const user = await currentUser()
if (!user) return null
return (
<div>
<h1>{user.firstName} {user.lastName}</h1>
<p>{user.emailAddresses[0]?.emailAddress}</p>
<img src={user.imageUrl} alt="Avatar" />
</div>
)
}
Pattern 2: Client-Side Hooks
'use client'
import { useUser, useAuth, useClerk, useSignIn } from '@clerk/nextjs'
export function ClientAuthExample() {
const { user, isLoaded, isSignedIn } = useUser() // Full user object
const { userId, getToken, signOut } = useAuth() // Auth state + token access
const { openSignIn, openUserProfile } = useClerk() // UI controls
if (!isLoaded) return <div>Loading...</div>
if (!isSignedIn) return <button onClick={() => openSignIn()}>Sign In</button>
const fetchWithAuth = async (url: string) => {
const token = await getToken()
return fetch(url, {
headers: { Authorization: `Bearer ${token}` },
})
}
return (
<div>
<p>Hello, {user.firstName}</p>
<button onClick={() => openUserProfile()}>Profile</button>
<button onClick={() => signOut()}>Sign Out</button>
</div>
)
}
Pattern 3: Protected Routes with Middleware
// middleware.ts
import { clerkMiddleware, createRouteMatcher } from '@clerk/nextjs/server'
const isPublicRoute = createRouteMatcher([
'/',
'/pricing',
'/sign-in(.*)',
'/sign-up(.*)',
'/api/webhooks(.*)',
])
export default clerkMiddleware(async (auth, req) => {
if (!isPublicRoute(req)) {
await auth.protect()
}
})
Pattern 4: Organization-Aware Querie
Implement security best practices with Clerk authentication.
Clerk Security Basics
Overview
Implement security best practices for Clerk authentication: environment variable protection, middleware hardening, API route defense, webhook verification, and session security.
Prerequisites
- Clerk SDK installed and configured
- Understanding of OWASP authentication best practices
- Production deployment planned or active
Instructions
Step 1: Secure Environment Variables
# .env.local — never commit this file
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_live_... # Safe to expose (public)
CLERK_SECRET_KEY=sk_live_... # NEVER expose client-side
CLERK_WEBHOOK_SECRET=whsec_... # Server-only
# .gitignore — ensure secrets stay out of git
.env.local
.env.*.local
.env.production
Validate at startup that secret keys are not leaked:
// lib/security-check.ts
export function assertServerOnly() {
if (typeof window !== 'undefined') {
throw new Error('This module must only be used server-side')
}
if (!process.env.CLERK_SECRET_KEY) {
throw new Error('CLERK_SECRET_KEY is not configured')
}
}
Step 2: Hardened Middleware Configuration
// middleware.ts
import { clerkMiddleware, createRouteMatcher } from '@clerk/nextjs/server'
import { NextResponse } from 'next/server'
const isPublicRoute = createRouteMatcher([
'/',
'/sign-in(.*)',
'/sign-up(.*)',
'/api/webhooks(.*)',
])
export default clerkMiddleware(async (auth, req) => {
// Protect all non-public routes
if (!isPublicRoute(req)) {
await auth.protect()
}
// Add security headers
const response = NextResponse.next()
response.headers.set('X-Frame-Options', 'DENY')
response.headers.set('X-Content-Type-Options', 'nosniff')
response.headers.set('Referrer-Policy', 'strict-origin-when-cross-origin')
response.headers.set(
'Content-Security-Policy',
"frame-ancestors 'none'; form-action 'self' https://clerk.com https://*.clerk.accounts.dev"
)
return response
})
Step 3: Secure API Routes
// app/api/admin/route.ts
import { auth } from '@clerk/nextjs/server'
import { NextRequest } from 'next/server'
export async function POST(req: NextRequest) {
const { userId, has } = await auth()
// 1. Verify authentication
if (!userId) {
return Response.json({ error: 'Unauthorized' }, { status: 401 })
}
// 2. Verify authorization (permission-based, not role-based)
if (!has({ permission: 'org:admin:access' })) {
return Response.json({ error: 'Forbidden' }, { status: 403 })
}
//Manage Clerk SDK version upgrades and handle breaking changes.
Clerk Upgrade & Migration
Current State
!npm list @clerk/nextjs @clerk/clerk-react @clerk/express 2>/dev/null | grep clerk || echo 'No Clerk packages found'
Overview
Safely upgrade Clerk SDK versions and handle breaking changes. Covers version checking, upgrade procedures, common migration patterns, and rollback planning.
Prerequisites
- Current Clerk integration working
- Git repository with clean working state
- Test environment available for validation
Instructions
Step 1: Check Current Version and Available Updates
# Check installed version
npm list @clerk/nextjs
# Check latest available
npm view @clerk/nextjs version
# Check all Clerk packages and their versions
npm outdated | grep clerk
Step 2: Review Breaking Changes
# View changelog for the target version
npx open-cli https://clerk.com/changelog
# Check GitHub releases for migration notes
npx open-cli https://github.com/clerk/javascript/releases
Key version milestones to watch for:
- v5 to v6:
auth()became async (mustawait auth()) - v5 to v6:
authMiddlewarerenamed toclerkMiddleware - v5 to v6: Import paths changed to
@clerk/nextjs/server
Step 3: Upgrade Process
# Create upgrade branch
git checkout -b chore/upgrade-clerk
# Upgrade all Clerk packages together (they must version-match)
npm install @clerk/nextjs@latest @clerk/themes@latest
# If using other Clerk packages:
# npm install @clerk/clerk-react@latest @clerk/express@latest @clerk/backend@latest
# Verify no version mismatches
npm list | grep clerk
Step 4: Handle Common Migration Patterns
v5 to v6: auth() is now async
// BEFORE (v5): auth() was synchronous
// const { userId } = auth()
// AFTER (v6): auth() returns a Promise
const { userId } = await auth()
Find all affected files:
# Search for synchronous auth() calls that need await
grep -rn "const.*= auth()" --include="*.ts" --include="*.tsx" | grep -v "await"
v5 to v6: Middleware migration
// BEFORE (v5):
// import { authMiddleware } from '@clerk/nextjs'
// export default authMiddleware({ publicRoutes: ['/'] })
// AFTER (v6):
import { clerkMiddleware, createRouteMatcher } from '@clerk/nextjs/server'
const isPublicRoute = createRouteMatcher(['/'])
export default clerkMiddleware(async (auth, req) => {
if (!isPublicRoute(req)) {
await auth.protect()
}
})
Configure Clerk webhooks and handle authentication events.
Clerk Webhooks & Events
Overview
Configure and handle Clerk webhooks for user lifecycle events and data synchronization. Clerk uses Svix for webhook delivery with HMAC-SHA256 signature verification. As of 2025, Clerk provides a built-in verifyWebhook() helper in @clerk/backend alongside the manual Svix approach.
Prerequisites
- Clerk account with webhook endpoint configured in Dashboard
- HTTPS endpoint (use
ngrokfor local dev) CLERK_WEBHOOK_SECRETenvironment variable (starts withwhsec_)
Instructions
Step 1: Install Dependencies
# Option A: Use @clerk/backend's built-in verifyWebhook() (recommended)
# Already included with @clerk/nextjs — no extra install needed
# Option B: Manual Svix verification
npm install svix
Step 2: Create Webhook Endpoint (verifyWebhook — Recommended)
// app/api/webhooks/clerk/route.ts
import { verifyWebhook } from '@clerk/backend/webhooks'
import type { WebhookEvent } from '@clerk/nextjs/server'
export async function POST(req: Request) {
let evt: WebhookEvent
try {
evt = await verifyWebhook(req)
} catch (err) {
console.error('Webhook verification failed:', err)
return new Response('Invalid signature', { status: 400 })
}
return handleWebhookEvent(evt)
}
Step 2 (Alternative): Manual Svix Verification
// app/api/webhooks/clerk/route.ts
import { Webhook } from 'svix'
import { headers } from 'next/headers'
import type { WebhookEvent } from '@clerk/nextjs/server'
export async function POST(req: Request) {
const WEBHOOK_SECRET = process.env.CLERK_WEBHOOK_SECRET
if (!WEBHOOK_SECRET) {
throw new Error('Missing CLERK_WEBHOOK_SECRET env variable')
}
const headerPayload = await headers()
const svixHeaders = {
'svix-id': headerPayload.get('svix-id') || '',
'svix-timestamp': headerPayload.get('svix-timestamp') || '',
'svix-signature': headerPayload.get('svix-signature') || '',
}
if (!svixHeaders['svix-id'] || !svixHeaders['svix-signature']) {
return new Response('Missing svix headers', { status: 400 })
}
// CRITICAL: Use req.text(), NOT req.json() — JSON parsing alters the payload
// and breaks signature verification
const body = await req.text()
const wh = new Webhook(WEBHOOK_SECRET)
let evt: WebhookEvent
try {
evt = wh.verify(body, svixHeaders) as WebhookEvent
} catch (err) {
console.error('Webhook verification failed:', err)
return new Response('Invalid signature', { status: 400 })
}
return handleWebhookEvent(evt)
}
Step 3: Implement Event Hand
How It Works
Skills trigger automatically when you discuss Clerk topics:
- "Help me set up Clerk" ->
clerk-install-auth - "Fix this Clerk error" ->
clerk-common-errors - "Deploy my Clerk app" ->
clerk-deploy-integration - "Migrate from Auth0 to Clerk" ->
clerk-migration-deep-dive
Ready to use clerk-pack?
Related Plugins
supabase-pack
Complete Supabase integration skill pack with 30 skills covering authentication, database, storage, realtime, edge functions, and production operations. Flagship+ tier vendor pack.
/plugin install supabase-pack@claude-code-plugins-plus
vercel-pack
Complete Vercel integration skill pack with 30 skills covering deployments, edge functions, preview environments, performance optimization, and production operations. Flagship+ tier vendor pack.
/plugin install vercel-pack@claude-code-plugins-plus
clay-pack
Complete Clay integration skill pack with 30 skills covering data enrichment, waterfall workflows, AI agents, and GTM automation. Flagship+ tier vendor pack.
/plugin install clay-pack@claude-code-plugins-plus
cursor-pack
Complete Cursor integration skill pack with 30 skills covering AI code editing, composer workflows, codebase indexing, and productivity features. Flagship+ tier vendor pack.
/plugin install cursor-pack@claude-code-plugins-plus
exa-pack
Complete Exa integration skill pack with 30 skills covering neural search, semantic retrieval, web search API, and AI-powered discovery. Flagship+ tier vendor pack.
/plugin install exa-pack@claude-code-plugins-plus
firecrawl-pack
Complete Firecrawl integration skill pack with 30 skills covering web scraping, crawling, markdown conversion, and LLM-ready data extraction. Flagship+ tier vendor pack.
/plugin install firecrawl-pack@claude-code-plugins-plus