Claude Code for Clerk Auth — Workflow Guide
The Setup
You are adding authentication, user management, and organization support to a Next.js application using Clerk. Clerk provides pre-built UI components, session management, and webhook-driven user sync. Claude Code can implement auth flows and protected routes, but it defaults to building custom auth from scratch or using NextAuth patterns that do not apply to Clerk.
What Claude Code Gets Wrong By Default
-
Builds custom login forms with bcrypt. Claude creates email/password forms, hashes passwords, and manages sessions manually. Clerk provides
<SignIn />and<SignUp />components that handle the entire auth UI including OAuth, MFA, and email verification. -
Uses NextAuth middleware patterns. Claude writes
withAuth()wrappers orgetServerSession()calls. Clerk uses its own middleware viaclerkMiddleware()inmiddleware.tsandauth()helper for server-side session access. -
Stores user data in a local database on signup. Claude creates a users table and inserts records during registration. Clerk manages user data in its own service — sync to your database using Clerk webhooks (
user.created,user.updatedevents), not during the auth flow. -
Ignores Clerk’s organization features. Claude builds team/org management from scratch. Clerk has built-in organization support with roles, invitations, and
<OrganizationSwitcher />components.
The CLAUDE.md Configuration
# Clerk Auth Next.js Project
## Architecture
- Auth: Clerk (@clerk/nextjs)
- Framework: Next.js 14 App Router
- Middleware: clerkMiddleware() in middleware.ts
- User sync: Clerk webhooks to local database
## Clerk Rules
- Use <SignIn />, <SignUp /> components — never build custom auth forms
- Protect routes in middleware.ts with clerkMiddleware()
- Access user in server components: auth() from @clerk/nextjs/server
- Access user in client components: useUser() from @clerk/nextjs
- Webhook sync: /api/webhooks/clerk endpoint handles user.created events
- Verify webhook signatures with svix package
- Environment: NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY, CLERK_SECRET_KEY
## Conventions
- ClerkProvider wraps app in layout.tsx
- Public routes listed in middleware.ts matcher config
- User DB sync happens via webhook only, never in auth flow
- Use clerk.users.getUser(userId) for server-side user lookup
- Organization ID from auth().orgId in multi-tenant routes
- Never store passwords — Clerk handles all credential management
Workflow Example
You want to add protected API routes and user-specific data. Prompt Claude Code:
“Protect the /api/projects endpoint so only authenticated users can access it. Return projects belonging to the current user. Add a webhook handler to sync new Clerk users to our database.”
Claude Code should use auth() from @clerk/nextjs/server in the API route to get userId, query projects filtered by that ID, and create a /api/webhooks/clerk endpoint that verifies the Svix signature and inserts new users on the user.created event.
Common Pitfalls
-
Missing the
<ClerkProvider>wrapper. Claude adds Clerk hooks in components but forgets the provider inlayout.tsx. All Clerk hooks (useUser,useAuth,useOrganization) fail silently without the provider wrapping the component tree. -
Webhook signature verification skipped. Claude processes webhook payloads without verifying the Svix signature. This allows anyone to fake webhook events. Always verify using the
svixpackage with your webhook signing secret. -
Using
currentUser()in client components. Claude calls the server-onlycurrentUser()function in client components, causing build errors. UseuseUser()hook in client components andcurrentUser()orauth()only in server components and API routes.