Clerk managed authentication - ClerkProvider, middleware, pre-built components, hooks, server-side auth, organizations, webhooks
Scanned 9/5/2026
Install to Claude Code
npx -y skills add agents-inc/skills --skill api-auth-clerk --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Api Auth Clerk?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/agents-inc-api-auth-clerk-skills)More formats (shields.io, HTML) on the badges page.
---
name: api-auth-clerk
description: Clerk managed authentication - ClerkProvider, middleware, pre-built components, hooks, server-side auth, organizations, webhooks
---
# Clerk Authentication Patterns
> **Quick Guide:** Clerk provides managed authentication with pre-built UI components, server-side helpers, and organization-based multi-tenancy. Use `clerkMiddleware()` for route protection, `<Show>` for conditional rendering, hooks for client state, and `auth()`/`currentUser()` for server-side auth. Clerk Core 3 (2026) replaces `<SignedIn>`/`<SignedOut>` with `<Show>`, renames the middleware file to `proxy.ts` (Next.js 16+), and consolidates packages.
---
<critical_requirements>
## CRITICAL: Before Using This Skill
> **All code must follow project conventions in CLAUDE.md** (kebab-case, named exports, import ordering, `import type`, named constants)
**(You MUST use `@clerk/nextjs/server` for ALL server-side imports -- NEVER import server helpers from `@clerk/nextjs`)**
**(You MUST verify webhooks using Clerk's `verifyWebhook` helper -- NEVER trust unverified webhook payloads)**
**(You MUST use `<Show>` component instead of deprecated `<SignedIn>`/`<SignedOut>`/`<Protect>` -- these are removed in Core 3)**
**(You MUST NOT pass the full `currentUser()` object to the client -- it contains `privateMetadata` that must stay server-side)**
**(You MUST protect routes in BOTH middleware AND data access layer -- middleware alone is insufficient)**
</critical_requirements>
---
**Auto-detection:** Clerk, ClerkProvider, clerkMiddleware, @clerk/nextjs, useUser, useAuth, useClerk, useSession, useOrganization, SignIn, SignUp, UserButton, UserProfile, OrganizationSwitcher, auth(), currentUser(), CLERK_SECRET_KEY, NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY, Show when="signed-in"
**When to use:**
- Adding authentication and user management to an application
- Building multi-tenant B2B apps with organization-based access control
- Using pre-built sign-in/sign-up UI components with customizable theming
- Protecting routes with middleware and server-side authorization checks
- Syncing Clerk user data to your database via webhooks
**Key patterns covered:**
- ClerkProvider setup, environment variables, middleware configuration
- Pre-built UI components (`<SignIn>`, `<SignUp>`, `<UserButton>`, `<Show>`)
- Client-side hooks (`useUser`, `useAuth`, `useSession`, `useOrganization`)
- Server-side auth (`auth()`, `currentUser()`) in Server Components, Route Handlers, Server Actions
- Middleware route protection with `clerkMiddleware()` and `createRouteMatcher()`
- Organization-based multi-tenancy with roles and permissions
- Webhook handling with Svix signature verification
**When NOT to use:**
- Self-hosted auth requirement (need full control over auth data storage)
- Cannot use a third-party auth service (compliance/regulatory constraints)
- Simple API key authentication (custom middleware is sufficient)
- Budget constraints prevent using a managed service
**Detailed Resources:**
- [reference.md](reference.md) - Decision frameworks, hooks quick reference, Core 3 migration cheat sheet
- [examples/core.md](examples/core.md) - ClerkProvider, environment variables, middleware configuration
- [examples/components.md](examples/components.md) - Pre-built components, customization, appearance prop
- [examples/hooks.md](examples/hooks.md) - useUser, useAuth, useSession, loading states, conditional rendering
- [examples/server.md](examples/server.md) - Server Components, API routes, Server Actions, webhook handling
- [examples/organizations.md](examples/organizations.md) - Organization management, roles, permissions, RBAC
---
<philosophy>
## Philosophy
Clerk is a **managed authentication platform** that handles the entire auth lifecycle: sign-up, sign-in, session management, user profiles, organizations, and MFA. Instead of building auth from scratch, you integrate Clerk's SDK and pre-built components.
**Core principles:**
1. **Defense in depth** -- Protect routes at the middleware layer AND verify auth at every data access point. Middleware alone is insufficient (CVE-2025-29927 demonstrated middleware bypass vulnerabilities).
2. **Server-first auth** -- Use `auth()` and `currentUser()` in Server Components and Route Handlers. Only use client hooks (`useUser`, `useAuth`) when you need reactive client-side state.
3. **Pre-built over custom** -- Use Clerk's `<SignIn>`, `<SignUp>`, `<UserButton>` components. Only build custom flows when the pre-built components genuinely cannot meet requirements.
4. **Organizations for multi-tenancy** -- Use Clerk Organizations with roles and permissions for B2B apps. Do not build custom tenant systems on top of Clerk's user model.
5. **Webhook-driven sync** -- Sync Clerk data to your database via webhooks, not by polling. Always verify webhook signatures with `verifyWebhook`.
**When to use Clerk:**
- You need auth quickly with minimal custom code
- You want pre-built UI components for sign-in/sign-up/user management
- You need organization-based multi-tenancy with RBAC
- You want managed MFA, SSO (SAML/OIDC), and social login
**When NOT to use Clerk:**
- You need full control over auth data storage (self-hosted requirement)
- You cannot use a third-party auth service (compliance/regulatory)
- Your app only needs simple API key authentication
- Budget constraints prevent using a managed service
</philosophy>
---
<patterns>
## Core Patterns
### Pattern 1: ClerkProvider and Middleware Setup
Every Clerk app needs `<ClerkProvider>` wrapping the app and `clerkMiddleware()` protecting routes. See [examples/core.md](examples/core.md) for full setup examples.
**Key rules:**
- `ClerkProvider` goes inside `<body>`, not wrapping `<html>` -- Core 3 requires this
- Use `NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY` env var, never hardcode keys
- Middleware file is `proxy.ts` on Next.js 16+ or `middleware.ts` on Next.js <=15
- Webhook endpoint must be in the public routes list (verified separately by `verifyWebhook`)
- `CLERK_WEBHOOK_SIGNING_SECRET` is the env var for webhook signing (not `CLERK_WEBHOOK_SECRET`)
```ts
// proxy.ts (Next.js 16+) or middleware.ts (Next.js <=15)
const isPublicRoute = createRouteMatcher([
"/",
"/sign-in(.*)",
"/sign-up(.*)",
"/api/webhooks(.*)",
]);
export default clerkMiddleware(async (auth, req) => {
if (!isPublicRoute(req)) {
await auth.protect();
}
});
```
**Why good:** Public routes explicitly whitelisted, everything else requires auth, webhook endpoint is public (verified separately)
```ts
// BAD: clerkMiddleware() with no callback
export default clerkMiddleware(); // All routes are PUBLIC by default!
```
**Why bad:** `clerkMiddleware()` without callback attaches auth data but does not protect any route
---
### Pattern 2: Pre-Built UI Components and `<Show>`
Use `<Show>` for conditional rendering (Core 3 replacement for `<SignedIn>`/`<SignedOut>`/`<Protect>`). See [examples/components.md](examples/components.md) for full examples.
```tsx
<Show when="signed-out"><SignInButton /></Show>
<Show when="signed-in"><UserButton /></Show>
<Show when={{ role: "org:admin" }}><AdminPanel /></Show>
<Show when={(has) => has({ permission: "org:invoices:manage" })}><InvoiceManager /></Show>
```
**Why good:** `<Show>` is the Core 3 API, supports string/object/callback conditions, `fallback` prop for unauthorized
```tsx
// BAD: Deprecated -- removed in Core 3
<SignedIn>...</SignedIn>
<SignedOut>...</SignedOut>
<Protect role="admin">...</Protect>
```
**Why bad:** `<SignedIn>`, `<SignedOut>`, `<Protect>` removed in Core 3, use `<Show>` with `when` prop
Sign-in/sign-up pages require catch-all route segments `[[...sign-in]]` for multi-step flows (MFA, OAuth callbacks).
---
### Pattern 3: Client-Side Hooks
Use hooks in Client Components for reactive auth state. **Always check `isLoaded` before accessing data.** See [examples/hooks.md](examples/hooks.md) for complete examples.
```tsx
"use client";
const { isLoaded, isSignedIn, user } = useUser();
if (!isLoaded) return <div>Loading...</div>;
if (!isSignedIn) return <div>Please sign in</div>;
// Now safe to access user.firstName, user.emailAddresses, etc.
```
**Why good:** Prevents hydration errors from accessing `undefined` during init, prevents TypeError from accessing `null` when signed out
```tsx
// BAD: Accessing user without guards
const { user } = useUser();
return <p>{user.firstName}</p>; // Runtime error when isLoaded=false or isSignedIn=false
```
**Why bad:** `user` is `undefined` until loaded and `null` when signed out
**Key hooks:**
- `useUser()` -- user profile data (name, email, avatar)
- `useAuth()` -- session tokens (`getToken()`), `userId`, `orgId`, `has()` for authorization
- `useOrganization()` -- active org data, membership, paginated members list
- `useReverification()` -- re-authenticate before sensitive actions (Core 3)
In Core 3, `getToken()` throws `ClerkOfflineError` when offline instead of returning null -- always wrap in try/catch.
---
### Pattern 4: Server-Side Authentication
Use `auth()` and `currentUser()` from `@clerk/nextjs/server`. See [examples/server.md](examples/server.md) for complete examples.
**`auth()` -- lightweight, no API call, reads session claims:**
```tsx
import { auth } from "@clerk/nextjs/server";
const { userId, orgId, isAuthenticated, redirectToSignIn } = await auth();
if (!isAuthenticated) return redirectToSignIn();
```
**`currentUser()` -- full user object, makes Backend API call:**
```tsx
import { currentUser } from "@clerk/nextjs/server";
const user = await currentUser();
// CRITICAL: Pick safe fields before passing to client components
const safeData = { firstName: user.firstName, imageUrl: user.imageUrl };
```
**Why critical:** `currentUser()` returns `privateMetadata` that must never reach the client. Explicitly pick fields.
**Defense in depth:** Auth MUST be checked in every Server Action and Route Handler, not just middleware:
```ts
"use server";
const { userId } = await auth();
if (!userId) throw new Error("Unauthorized");
// userId scopes all queries/mutations to current user
```
---
### Pattern 5: Authorization with Roles and Permissions
Use `auth.protect()` or `has()` for granular access control. See [examples/organizations.md](examples/organizations.md) for RBAC patterns.
```ts
// Middleware: role-based route protection
await auth.protect((has) => has({ role: "org:admin" }));
// Server Component: permission check
const { has } = await auth();
if (!has({ permission: "org:invoices:delete" })) redirect("/dashboard");
// Client: conditional rendering by role
<Show when={{ role: "org:admin" }}><AdminPanel /></Show>
```
**Permission format:** `org:<feature>:<action>` (e.g., `org:invoices:create`, `org:reports:read`)
**Default roles:** `org:admin` (all permissions), `org:member` (read-only). Custom roles configured in Clerk Dashboard.
---
### Pattern 6: Webhook Handling
Use Clerk webhooks (via Svix) to sync user data to your database. See [examples/server.md](examples/server.md) for complete handler.
```ts
import { verifyWebhook } from "@clerk/nextjs/webhooks";
export async function POST(req: Request) {
const evt = await verifyWebhook(req); // reads CLERK_WEBHOOK_SIGNING_SECRET env var
// evt.type: "user.created" | "user.updated" | "user.deleted" | "organization.*" | ...
// evt.data: UserJSON | OrganizationJSON | etc.
}
```
**Why good:** `verifyWebhook` validates Svix signature, always returns 200 to prevent retries, webhook route is public in middleware (verified by signature not by auth)
```ts
// BAD: No signature verification
const body = await req.json(); // Anyone can POST fake data!
await db.insert(users).values(body.data); // Security vulnerability
```
**Why bad:** No signature verification means anyone can send fake webhook events to your endpoint
**Import types from `@clerk/shared/types` (Core 3):** `UserJSON`, `OrganizationJSON`, `OrganizationMembershipJSON`
</patterns>
---
<decision_framework>
## Decision Framework
### Client vs Server Auth
```
Where do you need auth data?
|-- Server Component, Route Handler, Server Action
| |-- Need just userId/sessionId? --> auth()
| |-- Need full user object? --> currentUser()
| |-- Need to protect the route? --> auth.protect()
| +-- Need org context? --> auth() returns orgId, orgRole
|
+-- Client Component (interactive UI)
|-- Need user profile data? --> useUser()
|-- Need session/token data? --> useAuth()
|-- Need org data? --> useOrganization()
+-- Need low-level Clerk API? --> useClerk()
```
### Route Protection Strategy
```
What kind of route is it?
|-- Public (landing, sign-in, sign-up, webhooks)
| +-- Add to isPublicRoute matcher, skip auth.protect()
|
|-- Authenticated (dashboard, profile, settings)
| +-- auth.protect() in middleware + auth() check in data layer
|
+-- Authorized (admin, org-specific, permission-gated)
|-- Role-based? --> auth.protect({ role: "org:admin" })
+-- Permission-based? --> auth.protect((has) => has({ permission: "org:feature:action" }))
```
### Component Choice
```
What auth UI do you need?
|-- Full sign-in page --> <SignIn /> on catch-all route
|-- Full sign-up page --> <SignUp /> on catch-all route
|-- Sign-in button (modal) --> <SignInButton />
|-- User avatar + menu --> <UserButton />
|-- Full profile editor --> <UserProfile />
|-- Org switcher --> <OrganizationSwitcher />
+-- Conditional content --> <Show when="signed-in"> or <Show when={{ role: "..." }}>
```
</decision_framework>
---
<red_flags>
## RED FLAGS
**High Priority Issues:**
- Importing server helpers from `@clerk/nextjs` instead of `@clerk/nextjs/server` (breaks in Server Components)
- Using deprecated `<SignedIn>`/`<SignedOut>`/`<Protect>` components (removed in Core 3)
- Passing full `currentUser()` object to client components (leaks `privateMetadata`)
- Trusting webhook payloads without `verifyWebhook` signature verification
- Relying solely on middleware for route protection (middleware can be bypassed)
- Importing types from `@clerk/types` instead of `@clerk/shared/types` (Core 3 rename)
**Medium Priority Issues:**
- Not checking `isLoaded` before accessing hook data (causes hydration errors)
- Using `currentUser()` on the client side (it is server-only)
- Hardcoding Clerk keys instead of using environment variables
- Using `authMiddleware()` (deprecated, replaced by `clerkMiddleware()`)
- Not making webhook endpoint public in middleware matcher
- Using `CLERK_WEBHOOK_SECRET` instead of `CLERK_WEBHOOK_SIGNING_SECRET`
**Common Mistakes:**
- Naming middleware file `middleware.ts` on Next.js 16+ (should be `proxy.ts`) or `proxy.ts` on Next.js <=15 (should be `middleware.ts`)
- Forgetting catch-all segments `[[...sign-in]]` on sign-in/sign-up pages (breaks multi-step flows)
- Using `getToken()` without try/catch in Core 3 (throws `ClerkOfflineError` when offline instead of returning null)
- Not adding `prefetch={false}` to `<Link>` components pointing at protected routes from public pages
**Gotchas & Edge Cases:**
- `currentUser()` counts against Backend API rate limits -- prefer `useUser()` hook on the client when possible
- `auth()` in Server Components is deduplicated per request (safe to call multiple times)
- `<Show when={{ role: "org:admin" }}>` requires an active organization in the session
- Organization roles use the `org:` prefix (e.g., `org:admin`, `org:member`, `org:billing`)
- Clerk Core 3 requires Node.js 20.9.0+, Next.js 15.2.3+
- `@clerk/clerk-react` renamed to `@clerk/react` in Core 3 -- update imports after upgrade
</red_flags>
---
<critical_reminders>
## CRITICAL REMINDERS
> **All code must follow project conventions in CLAUDE.md** (kebab-case, named exports, import ordering, `import type`, named constants)
**(You MUST use `@clerk/nextjs/server` for ALL server-side imports -- NEVER import server helpers from `@clerk/nextjs`)**
**(You MUST verify webhooks using Clerk's `verifyWebhook` helper -- NEVER trust unverified webhook payloads)**
**(You MUST use `<Show>` component instead of deprecated `<SignedIn>`/`<SignedOut>`/`<Protect>` -- these are removed in Core 3)**
**(You MUST NOT pass the full `currentUser()` object to the client -- it contains `privateMetadata` that must stay server-side)**
**(You MUST protect routes in BOTH middleware AND data access layer -- middleware alone is insufficient)**
**Failure to follow these rules will create authentication vulnerabilities or break on Clerk Core 3.**
</critical_reminders>
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!