jackspace/claudeskillz · Archived

clerk-auth

>- Integrates Clerk authentication in React/Vite, Next.js App Router, and Cloudflare Workers apps. Covers ClerkProvider setup, clerkMiddleware and createRouteMatcher for protected routes, token verification with @clerk/backend verifyToken, custom JWT templates and session claims (user.public_metadata, org_id, org_role) for RBAC, Hono integration via @hono/clerk-auth, shadcn/ui auth components, and Playwright E2E testing with @clerk/testing, +clerk_test emails and the 424242 OTP. Documents 11 pi…

First seen Jan 24, 2026

Installation

$ npx skills add jackspace/claudeskillz --skill clerk-auth

Stronger alternatives

This repository is archived — consider an actively maintained alternative.

Similar popular skills

Related neighbors and high-traction skills in the same topics — useful to compare before installing.

Also in this package

Other skills from jackspace/claudeskillz · top by installs.

npx skills add jackspace/claudeskillz

Browse all from jackspace/claudeskillz

More details

Agent compatibility

Declared targets from SKILL.md / docs. Unmarked agents are not listed — the skill may still install via the CLI.

Claude Code Not declared
Cursor Not declared
Codex Not declared
GitHub Copilot Not declared
Windsurf Not declared
Gemini CLI Not declared
Cline Not declared
OpenCode Not declared

Repository health

Stars 16
License LICENSE
Default branch master
Open issues 0
Status Archived

Skill metadata

Parsed from SKILL.md frontmatter.

LicenseMIT

Package contents

Files included with this skill beyond the listing page.

  • skill md SKILL.md 21,217 B
  • docs README.md 5,383 B
  • docs SUMMARY.md 907 B

History

  1. First seen on skills.sh
  2. First recorded snapshot · 38 installs

SKILL.md

Clerk Authentication

Status: Production Ready ✅ Last Updated: 2025-10-28 Dependencies: None Latest Versions: @clerk/[email protected], @clerk/[email protected], @clerk/[email protected], @clerk/[email protected]


Quick Start (10 Minutes)

Choose your framework:

  • [React (Vite)](#react-vite-setup) - ClerkProvider + hooks
  • [Next.js App Router](#nextjs-app-router-setup) - Middleware + async auth()
  • [Cloudflare Workers](#cloudflare-workers-setup) - Backend verification

React (Vite) Setup

1. Install Clerk

\\\bash npm install @clerk/clerk-react \\\

Latest Version: @clerk/[email protected] (verified 2025-10-22)

2. Configure ClerkProvider

Update \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'

// Get publishable key from environment const PUBLISHABLEKEY = import.meta.env.VITECLERKPUBLISHABLEKEY

if (!PUBLISHABLE_KEY) { throw new Error('Missing Publishable Key') }

ReactDOM.createRoot(document.getElementById('root')!).render( <React.StrictMode> <ClerkProvider publishableKey={PUBLISHABLE_KEY}> <App /> </ClerkProvider> </React.StrictMode>, ) \\\`

CRITICAL:

3. Add Environment Variables

Create \.env.local\:

\\\bash VITECLERKPUBLISHABLEKEY=pktest_... \\\

Security Note: Only \VITE_\ prefixed vars are exposed to client code.

4. Use Authentication Hooks

\\\`typescript import { useUser, useAuth, useClerk } from '@clerk/clerk-react'

function App() { // Get user object (includes email, metadata, etc.) const { isLoaded, isSignedIn, user } = useUser()

// Get auth state and session methods const { userId, sessionId, getToken } = useAuth()

// Get Clerk instance for advanced operations const { openSignIn, signOut } = useClerk()

// Always check isLoaded before rendering auth-dependent UI if (!isLoaded) { return <div>Loading...</div> }

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

return ( <div> <h1>Welcome {user.firstName}!</h1> <p>Email: {user.primaryEmailAddress?.emailAddress}</p> <button onClick={() => signOut()}>Sign Out</button> </div> ) } \\\`

Why This Matters:


Next.js App Router Setup

1. Install Clerk

\\\bash npm install @clerk/nextjs \\\

Latest Version: @clerk/[email protected] (verified 2025-10-22)

2. Configure Environment Variables

Create \.env.local\:

\\\`bash NEXTPUBLICCLERKPUBLISHABLEKEY=pktest... CLERKSECRETKEY=sktest...

Optional: Customize sign-in/up pages

NEXTPUBLICCLERKSIGNINURL=/sign-in NEXTPUBLICCLERKSIGNUPURL=/sign-up NEXTPUBLICCLERKAFTERSIGNINURL=/dashboard NEXTPUBLICCLERKAFTERSIGNUPURL=/onboarding \\\`

CRITICAL:

3. Add Middleware for Route Protection

Create \middleware.ts\ in project root:

\\\`typescript import { clerkMiddleware, createRouteMatcher } from '@clerk/nextjs/server'

// Define which routes are public (everything else requires auth) const isPublicRoute = createRouteMatcher([ '/', '/sign-in(.)', '/sign-up(.)', '/api/webhooks(.*)', // Clerk webhooks should be public ])

export default clerkMiddleware(async (auth, request) => { // Protect all routes except public ones if (!isPublicRoute(request)) { 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|woff2?|ico|csv|docx?|xlsx?|zip|webmanifest)).)', // Always run for API routes '/(api|trpc)(.*)', ], } \\\`

CRITICAL:

4. Wrap App with ClerkProvider

Update \app/layout.tsx\:

\\\`typescript 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. Use auth() in Server Components

\\\`typescript import { auth, currentUser } from '@clerk/nextjs/server'

export default async function DashboardPage() { // Get auth state (lightweight) const { userId, sessionId } = await auth()

// Get full user object (heavier, fewer calls) const user = await currentUser()

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

return ( <div> <h1>Dashboard</h1> <p>User ID: {userId}</p> <p>Email: {user?.primaryEmailAddress?.emailAddress}</p> </div> ) } \\\`

CRITICAL:

  • \auth()\ is async in v6 (breaking change)
  • Use \auth()\ for lightweight checks
  • Use \currentUser()\ when you need full user object

Cloudflare Workers Setup

1. Install Dependencies

\\\bash npm install @clerk/backend hono \\\

Latest Versions:

2. Configure Environment Variables

Create \.dev.vars\ for local development:

\\\bash CLERKSECRETKEY=sktest... CLERKPUBLISHABLEKEY=pktest... \\\

Production: Use \wrangler secret put CLERKSECRETKEY\

3. Implement Token Verification

Create \src/index.ts\:

\\\`typescript import { Hono } from 'hono' import { verifyToken } from '@clerk/backend'

type Bindings = { CLERKSECRETKEY: string CLERKPUBLISHABLEKEY: string }

type Variables = { userId: string | null sessionClaims: any | null }

const app = new Hono<{ Bindings: Bindings; Variables: Variables }>()

// Middleware: Verify Clerk token 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.CLERKSECRETKEY, // IMPORTANT: Set authorizedParties to prevent CSRF attacks authorizedParties: ['https://yourdomain.com';], })

if (error) { console.error('Token verification failed:', error) c.set('userId', null) c.set('sessionClaims', null) } else { c.set('userId', data.sub) c.set('sessionClaims', data) } } catch (err) { console.error('Token verification error:', err) c.set('userId', null) c.set('sessionClaims', null) }

return next() })

// Protected route app.get('/api/protected', (c) => { const userId = c.get('userId')

if (!userId) { return c.json({ error: 'Unauthorized' }, 401) }

return c.json({ message: 'This is protected', userId, sessionClaims: c.get('sessionClaims'), }) })

export default app \\\`

CRITICAL:


JWT Templates & Custom Claims

Clerk allows customizing JWT (JSON Web Token) structure using templates. This enables integration with third-party services, role-based access control, and multi-tenant applications.

Quick Start: Create a JWT Template

1. Navigate to Clerk Dashboard:

  • Go to Sessions page
  • Click Customize session token
  • Click Create template

2. Define Template:

{
  "user_id": "{{user.id}}",
  "email": "{{user.primary_email_address}}",
  "role": "{{user.public_metadata.role || 'user'}}"
}

3. Use Template in Code:

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

Available Shortcodes

Category Shortcodes Example
User ID & Name {{user.id}}, {{user.firstname}}, {{user.lastname}}, {{user.full_name}} "John Doe"
Contact {{user.primaryemailaddress}}, {{user.primaryphoneaddress}} "[email protected]"
Profile {{user.imageurl}}, {{user.username}}, {{user.createdat}} "https://...";
Verification {{user.emailverified}}, {{user.phonenumber_verified}} true
Metadata {{user.publicmetadata}}, {{user.publicmetadata.FIELD}} {"role": "admin"}
Organization orgid, orgslug, org_role (in sessionClaims) "org:admin"

Advanced Features

String Interpolation:

{
  "full_name": "{{user.last_name}} {{user.first_name}}",
  "greeting": "Hello, {{user.first_name}}!"
}

Conditional Fallbacks:

{
  "role": "{{user.public_metadata.role || 'user'}}",
  "age": "{{user.public_metadata.age || 18}}",
  "verified": "{{user.email_verified || user.phone_number_verified}}"
}

Nested Metadata with Dot Notation:

{
  "interests": "{{user.public_metadata.profile.interests}}",
  "department": "{{user.public_metadata.department}}"
}

Default Claims (Auto-Included)

Every JWT includes these claims automatically (cannot be overridden):

{
  "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
}

Size Limitation: 1.2KB for Custom Claims

Problem: Browser cookies limited to 4KB. Clerk's default claims consume ~2.8KB, leaving 1.2KB for custom claims.

⚠️ Development Note: When testing custom 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](#issue-11-431-request-header-fields-too-large-vite-dev-mode) for solution.

Solution:

// ✅ 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.

TypeScript Type Safety

Add global type declarations for auto-complete:

Create types/globals.d.ts:

export {}

declare global {
  interface CustomJwtSessionClaims {
    metadata: {
      role?: 'admin' | 'moderator' | 'user'
      onboardingComplete?: boolean
      organizationId?: string
    }
  }
}

Common Use Cases

Role-Based Access Control:

{
  "email": "{{user.primary_email_address}}",
  "role": "{{user.public_metadata.role || 'user'}}",
  "permissions": "{{user.public_metadata.permissions}}"
}

Multi-Tenant Applications:

{
  "user_id": "{{user.id}}",
  "org_id": "{{user.public_metadata.org_id}}",
  "org_role": "{{user.public_metadata.org_role}}"
}

Supabase Integration:

{
  "email": "{{user.primary_email_address}}",
  "app_metadata": {
    "provider": "clerk"
  },
  "user_metadata": {
    "full_name": "{{user.full_name}}"
  }
}

See Also

  • Complete Reference: See references/jwt-claims-guide.md for comprehensive documentation
  • Template Examples: See templates/jwt/ directory for working examples
  • TypeScript Types: See templates/typescript/custom-jwt-types.d.ts
  • Official Docs: https://clerk.com/docs/guides/sessions/jwt-templates

Testing

Clerk provides comprehensive testing tools for local development and CI/CD pipelines.

Quick Start: Test Credentials

Test Emails (no emails sent, fixed OTP):

[email protected]
[email protected]

Test Phone Numbers (no SMS sent, fixed OTP):

+12015550100
+19735550133

Fixed OTP Code: 424242 (works for all test credentials)

Generate Session Tokens

For testing API endpoints, generate valid session tokens (60-second lifetime):

# 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

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>

E2E Testing with Playwright

Install @clerk/testing for automatic Testing Token management:

npm install -D @clerk/testing

Global Setup (global.setup.ts):

import { clerkSetup } from '@clerk/testing/playwright'
import { test as setup } from '@playwright/test'

setup('global setup', async ({}) => {
  await clerkSetup()
})

Test File (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 Tokens (Bot Detection Bypass)

Testing Tokens bypass bot detection in test suites.

Obtain Token:

curl -X POST https://api.clerk.com/v1/testing_tokens \
  -H "Authorization: Bearer sk_test_..."

Use in Frontend API Requests:

POST https://your-app.clerk.accounts.dev/v1/client/sign_ups?__clerk_testing_token=TOKEN

Note: @clerk/testing handles this automatically for Playwright/Cypress.

Production Limitations

Testing Tokens work in both development and production, but:

  • ❌ Code-based auth (SMS/Email OTP) not supported in production
  • ✅ Email + password authentication supported
  • ✅ Magic links supported

See Also


Known Issues Prevention

This skill prevents 11 documented issues:

Issue #1: Missing Clerk Secret Key

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\

Issue #2: API Key → Secret Key Migration

Error: "apiKey is deprecated, use secretKey" Source: https://clerk.com/docs/upgrade-guides/core-2/backend Prevention: Replace \apiKey\ with \secretKey\ in all calls

Issue #3: JWKS Cache Race Condition

Error: "No JWK available" Source: https://github.com/clerk/javascript/blob/main/packages/backend/CHANGELOG.md Prevention: Use @clerk/[email protected] or later (fixed)

Issue #4: Missing authorizedParties (CSRF)

Error: No error, but CSRF vulnerability Source: https://clerk.com/docs/reference/backend/verify-token Prevention: Always set \authorizedParties: ['https://yourdomain.com']\

Issue #5: Import Path Changes (Core 2)

Error: "Cannot find module" Source: https://clerk.com/docs/upgrade-guides/core-2/backend Prevention: Update import paths for Core 2

Issue #6: JWT Size Limit Exceeded

Error: Token exceeds size limit Source: https://clerk.com/docs/backend-requests/making/custom-session-token Prevention: Keep custom claims under 1.2KB

Issue #7: Deprecated API Version v1

Error: "API version v1 is deprecated" Source: https://clerk.com/docs/upgrade-guides/core-2/backend Prevention: Use latest SDK versions (API v2025-04-10)

Issue #8: ClerkProvider JSX Component Error

Error: "cannot be used as a JSX component" Source: https://stackoverflow.com/questions/79265537 Prevention: Ensure React 19 compatibility with @clerk/[email protected]+

Issue #9: Async auth() Helper Confusion

Error: "auth() is not a function" Source: https://clerk.com/changelog/2024-10-22-clerk-nextjs-v6 Prevention: Always await: \const { userId } = await auth()\

Issue #10: Environment Variable Misconfiguration

Error: "Missing Publishable Key" or secret leaked Prevention: Use correct prefixes (\NEXTPUBLIC\, \VITE_\), never commit secrets

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

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).


Critical Rules

Always Do

✅ Set \authorizedParties\ when verifying tokens ✅ Use \CLERKSECRETKEY\ environment variable ✅ Check \isLoaded\ before rendering auth UI ✅ Use \getToken()\ fresh for each request ✅ Await \auth()\ in Next.js v6+ ✅ Use \NEXTPUBLIC\ prefix for client vars only ✅ Store secrets via \wrangler secret put\ ✅ Implement middleware for route protection ✅ Use API version 2025-04-10 or later

Never Do

❌ Store \CLERKSECRETKEY\ in client code ❌ Use deprecated \apiKey\ parameter ❌ Store tokens in localStorage ❌ Skip \authorizedParties\ check ❌ Exceed 1.2KB for custom JWT claims ❌ Forget to check \isLoaded\ ❌ Expose secrets with \NEXTPUBLIC\ prefix ❌ Use API version v1


Official Documentation


Package Versions (Verified 2025-10-22)

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