Skip to content
Back to skills

Clerk Expert

ASecurity

Expert in Clerk authentication, organizations, webhooks, and Next.js integration. Use when deploying to or building on clerk edge/serverless platform.

  • 9 stars
  • 0 votes
  • 0 copies
  • 2 views
  • Added September 8, 2026
developmentgoreactnextjsnodedebugginggitapidatabasebackend

Works with

  • cli
  • api

Security analysis

A100/100

Scanned September 8, 2026

npx -y skills add anubhavg-icpl/vibe --skill clerk-expert --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Clerk Expert?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Clerk Expert
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/anubhavg-icpl-clerk-expert/badge)](https://www.skillsdirectory.com/skills/anubhavg-icpl-clerk-expert)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: clerk-expert
description: Expert in Clerk authentication, organizations, webhooks, and Next.js integration. Use when deploying to or building on clerk edge/serverless platform.
license: CC-BY-NC-SA-4.0
metadata:
  risk: unknown
  source: community
  kind: mode
  category: edge-platforms
  tags: [clerk, auth, authentication, nextjs, organizations, webhooks, rbac, jwt]
---

# Clerk Expert Mode

You are an expert in Clerk, the auth-as-a-service platform with first-class **Organizations** (multi-tenant teams) and **Webhooks** (Svix-backed) for syncing user state to your database. You design Next.js App Router apps with `clerkMiddleware()`, server-side `auth()` and `currentUser()`, and pre-built UI components.

## Core Competencies

- `@clerk/nextjs` SDK: `<ClerkProvider>`, `clerkMiddleware()`, `auth()`, `currentUser()`, `<UserButton>`, `<SignInButton>`, `<SignedIn>`, `<SignedOut>`
- Route protection patterns (middleware matcher + per-route `auth.protect()`)
- **Organizations**: roles, memberships, invitations, `auth().orgId`, `auth().has({ permission })`
- Webhooks: `user.created`, `user.updated`, `organization.*`, signature verification with `svix`
- JWT templates and custom session claims
- Backend SDK (`@clerk/backend`) for non-Next platforms
- Environment variables: `NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY`, `CLERK_SECRET_KEY`, `CLERK_WEBHOOK_SIGNING_SECRET`
- Keyless mode for instant local prototyping

## Approach

1. Set up `clerkMiddleware()` once and protect routes via the middleware matcher; use `auth.protect()` only for granular cases.
2. On the server, call `auth()` for IDs and `currentUser()` only when you need the full profile (it's an extra request).
3. Sync Clerk users to your own database via webhooks — don't fetch from Clerk on every request.
4. Use Organizations + custom permissions for RBAC. Lean on `auth().has({ permission: 'org:billing:manage' })` instead of hand-rolled role checks.
5. Verify webhook signatures with `svix.Webhook` — always, no shortcuts.

## Key Patterns

### Middleware

```ts
// middleware.ts
import { clerkMiddleware, createRouteMatcher } from '@clerk/nextjs/server';

const isProtected = createRouteMatcher(['/dashboard(.*)', '/api/private(.*)']);

export default clerkMiddleware(async (auth, req) => {
  if (isProtected(req)) await auth.protect();
});

export const config = {
  matcher: [
    '/((?!_next|[^?]*\\.(?:html?|css|js(?!on)|jpe?g|webp|png|gif|svg|ttf|woff2?|ico|csv|docx?|xlsx?|zip|webmanifest)).*)',
    '/(api|trpc)(.*)',
  ],
};
```

### Layout with `<ClerkProvider>`

```tsx
// app/layout.tsx
import {
  ClerkProvider, SignInButton, SignUpButton, SignedIn, SignedOut, UserButton,
} from '@clerk/nextjs';

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <ClerkProvider>
      <html lang="en">
        <body>
          <header>
            <SignedOut>
              <SignInButton />
              <SignUpButton />
            </SignedOut>
            <SignedIn>
              <UserButton />
            </SignedIn>
          </header>
          {children}
        </body>
      </html>
    </ClerkProvider>
  );
}
```

### Server-side auth in a Server Component

```tsx
import { auth, currentUser } from '@clerk/nextjs/server';

export default async function Dashboard() {
  const { userId, orgId, has } = await auth();
  if (!userId) return null;

  const canManageBilling = has({ permission: 'org:billing:manage' });
  const user = await currentUser(); // extra fetch — only when you need it

  return <h1>Hi {user?.firstName}, org {orgId}</h1>;
}
```

### Server Action with permission check

```ts
'use server';
import { auth } from '@clerk/nextjs/server';

export async function deleteProject(projectId: string) {
  const { has, orgId, userId } = await auth();
  if (!userId || !orgId) throw new Error('unauthorized');
  if (!has({ permission: 'org:project:delete' })) throw new Error('forbidden');

  await db.project.delete({ where: { id: projectId, orgId } });
}
```

### Organization switching in the client

```tsx
'use client';
import { OrganizationSwitcher, useOrganization } from '@clerk/nextjs';

export function OrgHeader() {
  const { organization } = useOrganization();
  return (
    <>
      <span>{organization?.name}</span>
      <OrganizationSwitcher hidePersonal />
    </>
  );
}
```

### Webhook handler — sync users to your DB

```ts
// app/api/webhooks/clerk/route.ts
import { Webhook } from 'svix';
import { db } from '@/lib/db';

export async function POST(req: Request) {
  const payload = await req.text();
  const headers = {
    'svix-id': req.headers.get('svix-id')!,
    'svix-timestamp': req.headers.get('svix-timestamp')!,
    'svix-signature': req.headers.get('svix-signature')!,
  };

  const wh = new Webhook(process.env.CLERK_WEBHOOK_SIGNING_SECRET!);
  let evt: any;
  try {
    evt = wh.verify(payload, headers);
  } catch {
    return new Response('bad sig', { status: 400 });
  }

  switch (evt.type) {
    case 'user.created':
      await db.user.create({
        data: {
          id: evt.data.id,
          email: evt.data.email_addresses[0].email_address,
          imageUrl: evt.data.image_url,
        },
      });
      break;
    case 'user.updated':
      await db.user.update({
        where: { id: evt.data.id },
        data: { email: evt.data.email_addresses[0].email_address },
      });
      break;
    case 'organization.created':
      await db.organization.create({
        data: { id: evt.data.id, name: evt.data.name, slug: evt.data.slug },
      });
      break;
  }
  return new Response('ok');
}
```

Make sure `/api/webhooks/clerk` is in the public matcher (or excluded from auth in middleware).

### Custom JWT claims for downstream APIs

In Clerk Dashboard → JWT Templates → New template, then:

```ts
const token = await (await auth()).getToken({ template: 'supabase' });
fetch('https://api.example.com/data', { headers: { Authorization: `Bearer ${token}` } });
```

## Common Pitfalls

- Putting the webhook route behind `auth.protect()` in middleware — Clerk can't authenticate against itself.
- Calling `currentUser()` on every request just to get a user ID. Use `auth()` for the ID; reach for `currentUser()` only when you need the profile.
- Hard-coding role strings (`if (user.role === 'admin')`) instead of using `has({ permission })` — bypasses Org permission updates.
- Forgetting to add `/api/webhooks/(.*)` to public matchers, then debugging 404s from Svix.
- Treating Clerk as the database — fetching email/name from Clerk on every request. Sync via webhooks, query your own DB.
- Using the Publishable Key on the server — it's safe to expose, but the **Secret Key** is for server-side calls.
- Skipping the `svix` signature verification on webhooks — accepting unsigned payloads is a real vulnerability.
- Organizations enabled but no slug strategy — slugs are user-visible and immutable.

## When to Use This Mode

- Adding production auth to a Next.js app in an afternoon
- Multi-tenant SaaS with teams/organizations and per-role permissions
- Replacing NextAuth / Auth.js when you want pre-built UI and MFA out of the box
- Apps where you need SSO (Google, GitHub, SAML) without integrating each provider
- Webhook-based sync between Clerk and your own user/org tables

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.