Get the FREE Ultimate OpenClaw Setup Guide →

convex-clerk

Scanned
npx machina-cli add skill PolarCoding85/convex-agent-skillz/convex-clerk-skill --openclaw
Files (1)
SKILL.md
5.9 KB

Convex + Clerk Authentication

Provider-specific patterns for integrating Clerk with Convex.

Required Configuration

1. auth.config.ts

// convex/auth.config.ts
import { AuthConfig } from 'convex/server';

export default {
  providers: [
    {
      domain: process.env.CLERK_JWT_ISSUER_DOMAIN!,
      applicationID: 'convex'
    }
  ]
} satisfies AuthConfig;

CRITICAL: JWT template in Clerk MUST be named exactly convex.

2. Environment Variables

# .env.local (Vite)
VITE_CLERK_PUBLISHABLE_KEY=pk_test_...

# .env.local (Next.js)
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_test_...
CLERK_SECRET_KEY=sk_test_...

# Convex Dashboard Environment Variables
CLERK_JWT_ISSUER_DOMAIN=https://verb-noun-00.clerk.accounts.dev
CLERK_WEBHOOK_SECRET=whsec_... # If using webhooks

Client Setup

React (Vite)

// src/main.tsx
import { ClerkProvider, useAuth } from "@clerk/clerk-react";
import { ConvexProviderWithClerk } from "convex/react-clerk";
import { ConvexReactClient } from "convex/react";

const convex = new ConvexReactClient(import.meta.env.VITE_CONVEX_URL);

ReactDOM.createRoot(document.getElementById("root")!).render(
  <ClerkProvider publishableKey={import.meta.env.VITE_CLERK_PUBLISHABLE_KEY}>
    <ConvexProviderWithClerk client={convex} useAuth={useAuth}>
      <App />
    </ConvexProviderWithClerk>
  </ClerkProvider>
);

Next.js App Router

// components/ConvexClientProvider.tsx
'use client';

import { ReactNode } from 'react';
import { ConvexReactClient } from 'convex/react';
import { ConvexProviderWithClerk } from 'convex/react-clerk';
import { useAuth } from '@clerk/nextjs';

const convex = new ConvexReactClient(process.env.NEXT_PUBLIC_CONVEX_URL!);

export default function ConvexClientProvider({ children }: { children: ReactNode }) {
  return (
    <ConvexProviderWithClerk client={convex} useAuth={useAuth}>
      {children}
    </ConvexProviderWithClerk>
  );
}
// app/layout.tsx
import { ClerkProvider } from '@clerk/nextjs';
import ConvexClientProvider from '@/components/ConvexClientProvider';

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html>
      <body>
        <ClerkProvider>
          <ConvexClientProvider>{children}</ConvexClientProvider>
        </ClerkProvider>
      </body>
    </html>
  );
}

Next.js Middleware

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

const isProtectedRoute = createRouteMatcher(['/dashboard(.*)']);

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

export const config = {
  matcher: ['/((?!.*\\..*|_next).*)', '/', '/(api|trpc)(.*)']
};

UI Components

Use Convex auth components, NOT Clerk's:

// ✅ Correct
import { Authenticated, Unauthenticated, AuthLoading } from 'convex/react';

// ❌ Don't use these for conditional rendering
import { SignedIn, SignedOut } from '@clerk/clerk-react';
import { SignInButton, UserButton } from "@clerk/clerk-react";
import { Authenticated, Unauthenticated } from "convex/react";

function App() {
  return (
    <>
      <Authenticated>
        <UserButton />
        <Content />
      </Authenticated>
      <Unauthenticated>
        <SignInButton />
      </Unauthenticated>
    </>
  );
}

Clerk Webhooks for User Sync

See WEBHOOKS.md for complete implementation.

Setup in Clerk Dashboard:

  1. Webhooks > Add Endpoint
  2. URL: https://your-deployment.convex.site/clerk-users-webhook
  3. Events: Select all user.* events
  4. Copy Signing Secret → Convex Dashboard env vars as CLERK_WEBHOOK_SECRET

Accessing User Info

Client-side (Clerk SDK)

import { useUser } from "@clerk/clerk-react";

function Profile() {
  const { user } = useUser();
  return <span>Hello, {user?.fullName}</span>;
}

Server-side (Convex functions)

export const myQuery = query({
  handler: async (ctx) => {
    const identity = await ctx.auth.getUserIdentity();
    // identity.name, identity.email, etc.
    // Fields depend on Clerk JWT template claims config
  }
});

Dev vs Prod Configuration

EnvironmentPublishable KeyIssuer Domain
Developmentpk_test_...https://verb-noun-00.clerk.accounts.dev
Productionpk_live_...https://clerk.your-domain.com

Set different values in Convex Dashboard for dev vs prod deployments.

Clerk-Specific Troubleshooting

IssueCauseFix
Token not generatedJWT template not named "convex"Rename template to exactly convex
aud mismatchWrong applicationIDUse applicationID: "convex"
iss mismatchWrong domainCopy Frontend API URL from Clerk
Webhook failsWrong secretCopy Signing Secret from Clerk webhook

DO ✅

  • Name JWT template exactly convex
  • Use ConvexProviderWithClerk with useAuth from Clerk
  • Use useConvexAuth() not Clerk's useAuth() for auth state
  • Use Convex's <Authenticated> not Clerk's <SignedIn>
  • Set CLERK_JWT_ISSUER_DOMAIN in Convex Dashboard

DON'T ❌

  • Rename the JWT template from "convex"
  • Use Clerk's auth hooks to gate Convex queries
  • Hardcode the issuer domain (use env var)
  • Forget to deploy after changing auth.config.ts

Source

git clone https://github.com/PolarCoding85/convex-agent-skillz/blob/main/.claude/skills/convex-clerk-skill/SKILL.mdView on GitHub

Overview

Clerk + Convex authentication integration. It helps you configure Clerk as an auth provider, wire ConvexProviderWithClerk, and handle Clerk webhooks for user sync.

How This Skill Works

Configures Convex to trust Clerk JWTs via auth.config.ts, with the issuer domain and applicationID 'convex'. The integration uses ConvexProviderWithClerk (and ClerkProvider where needed) to tie Clerk sessions to Convex clients, and enables Clerk webhooks for user sync and route protection via Clerk middleware.

When to Use It

  • Setting up Clerk as the auth provider for a Convex app (React or Next.js).
  • Configuring ConvexProviderWithClerk in your frontend (Vite/Next.js) and using Clerk's useAuth.
  • Implementing Clerk webhooks for user sync (clerk-users-webhook) and signing secret management.
  • Troubleshooting Clerk-specific auth issues, such as JWT template naming or issuer domain mismatches.
  • Protecting routes with Clerk middleware in Next.js and ensuring UI uses Convex auth components.

Quick Start

  1. Step 1: Configure Clerk in auth.config.ts with the Clerk issuer domain and applicationID 'convex'; ensure the JWT template is named convex.
  2. Step 2: Add environment variables (VITE_CLERK_PUBLISHABLE_KEY / NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY, CLERK_SECRET_KEY, CLERK_JWT_ISSUER_DOMAIN, CLERK_WEBHOOK_SECRET) and set them in your hosting environment.
  3. Step 3: Wrap your app with ClerkProvider and ConvexProviderWithClerk (and useAuth where needed); use Convex auth components for UI and implement Clerk webhooks if needed.

Best Practices

  • Name the Clerk JWT template convex exactly in Clerk to match Convex expectations.
  • Set CLERK_JWT_ISSUER_DOMAIN consistently in auth.config.ts and environment variables.
  • Store CLERK_SECRET_KEY and publishable keys securely; never commit them to code.
  • Wrap your app with ClerkProvider and ConvexProviderWithClerk; use Convex auth components instead of Clerk UI components.
  • Enable Clerk webhooks and copy the signing secret to the Convex Dashboard; verify webhook events.

Example Use Cases

  • A React (Vite) app integrating ClerkProvider with ConvexProviderWithClerk and useAuth.
  • A Next.js App Router setup using ConvexClientProvider and Clerk useAuth for authentication state.
  • Next.js middleware configured with clerkMiddleware to protect /dashboard routes.
  • Clerk webhooks endpoint at /clerk-users-webhook to sync Clerk users with Convex.
  • Troubleshooting a mismatch where the Clerk JWT template name is not convex, causing auth failures.

Frequently Asked Questions

Add this skill to your agents
Sponsor this space

Reach thousands of developers