$npx -y skills add PolarCoding85/convex-agent-skillz --skill convex-clerk-skillClerk authentication integration for Convex. Use when setting up Clerk auth, configuring ConvexProviderWithClerk, implementing Clerk webhooks for user sync, or troubleshooting Clerk-specific auth issues.
| 1 | # Convex + Clerk Authentication |
| 2 | |
| 3 | Provider-specific patterns for integrating Clerk with Convex. |
| 4 | |
| 5 | ## Required Configuration |
| 6 | |
| 7 | ### 1. auth.config.ts |
| 8 | |
| 9 | ```typescript |
| 10 | // convex/auth.config.ts |
| 11 | import { AuthConfig } from 'convex/server'; |
| 12 | |
| 13 | export default { |
| 14 | providers: [ |
| 15 | { |
| 16 | domain: process.env.CLERK_JWT_ISSUER_DOMAIN!, |
| 17 | applicationID: 'convex' |
| 18 | } |
| 19 | ] |
| 20 | } satisfies AuthConfig; |
| 21 | ``` |
| 22 | |
| 23 | **CRITICAL:** JWT template in Clerk MUST be named exactly `convex`. |
| 24 | |
| 25 | ### 2. Environment Variables |
| 26 | |
| 27 | ```bash |
| 28 | # .env.local (Vite) |
| 29 | VITE_CLERK_PUBLISHABLE_KEY=pk_test_... |
| 30 | |
| 31 | # .env.local (Next.js) |
| 32 | NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_test_... |
| 33 | CLERK_SECRET_KEY=sk_test_... |
| 34 | |
| 35 | # Convex Dashboard Environment Variables |
| 36 | CLERK_JWT_ISSUER_DOMAIN=https://verb-noun-00.clerk.accounts.dev |
| 37 | CLERK_WEBHOOK_SECRET=whsec_... # If using webhooks |
| 38 | ``` |
| 39 | |
| 40 | ## Client Setup |
| 41 | |
| 42 | ### React (Vite) |
| 43 | |
| 44 | ```typescript |
| 45 | // src/main.tsx |
| 46 | import { ClerkProvider, useAuth } from "@clerk/clerk-react"; |
| 47 | import { ConvexProviderWithClerk } from "convex/react-clerk"; |
| 48 | import { ConvexReactClient } from "convex/react"; |
| 49 | |
| 50 | const convex = new ConvexReactClient(import.meta.env.VITE_CONVEX_URL); |
| 51 | |
| 52 | ReactDOM.createRoot(document.getElementById("root")!).render( |
| 53 | <ClerkProvider publishableKey={import.meta.env.VITE_CLERK_PUBLISHABLE_KEY}> |
| 54 | <ConvexProviderWithClerk client={convex} useAuth={useAuth}> |
| 55 | <App /> |
| 56 | </ConvexProviderWithClerk> |
| 57 | </ClerkProvider> |
| 58 | ); |
| 59 | ``` |
| 60 | |
| 61 | ### Next.js App Router |
| 62 | |
| 63 | ```typescript |
| 64 | // components/ConvexClientProvider.tsx |
| 65 | 'use client'; |
| 66 | |
| 67 | import { ReactNode } from 'react'; |
| 68 | import { ConvexReactClient } from 'convex/react'; |
| 69 | import { ConvexProviderWithClerk } from 'convex/react-clerk'; |
| 70 | import { useAuth } from '@clerk/nextjs'; |
| 71 | |
| 72 | const convex = new ConvexReactClient(process.env.NEXT_PUBLIC_CONVEX_URL!); |
| 73 | |
| 74 | export default function ConvexClientProvider({ children }: { children: ReactNode }) { |
| 75 | return ( |
| 76 | <ConvexProviderWithClerk client={convex} useAuth={useAuth}> |
| 77 | {children} |
| 78 | </ConvexProviderWithClerk> |
| 79 | ); |
| 80 | } |
| 81 | ``` |
| 82 | |
| 83 | ```typescript |
| 84 | // app/layout.tsx |
| 85 | import { ClerkProvider } from '@clerk/nextjs'; |
| 86 | import ConvexClientProvider from '@/components/ConvexClientProvider'; |
| 87 | |
| 88 | export default function RootLayout({ children }: { children: React.ReactNode }) { |
| 89 | return ( |
| 90 | <html> |
| 91 | <body> |
| 92 | <ClerkProvider> |
| 93 | <ConvexClientProvider>{children}</ConvexClientProvider> |
| 94 | </ClerkProvider> |
| 95 | </body> |
| 96 | </html> |
| 97 | ); |
| 98 | } |
| 99 | ``` |
| 100 | |
| 101 | ### Next.js Middleware |
| 102 | |
| 103 | ```typescript |
| 104 | // middleware.ts |
| 105 | import { clerkMiddleware, createRouteMatcher } from '@clerk/nextjs/server'; |
| 106 | |
| 107 | const isProtectedRoute = createRouteMatcher(['/dashboard(.*)']); |
| 108 | |
| 109 | export default clerkMiddleware(async (auth, req) => { |
| 110 | if (isProtectedRoute(req)) { |
| 111 | await auth.protect(); |
| 112 | } |
| 113 | }); |
| 114 | |
| 115 | export const config = { |
| 116 | matcher: ['/((?!.*\\..*|_next).*)', '/', '/(api|trpc)(.*)'] |
| 117 | }; |
| 118 | ``` |
| 119 | |
| 120 | ## UI Components |
| 121 | |
| 122 | Use Convex auth components, NOT Clerk's: |
| 123 | |
| 124 | ```typescript |
| 125 | // ✅ Correct |
| 126 | import { Authenticated, Unauthenticated, AuthLoading } from 'convex/react'; |
| 127 | |
| 128 | // ❌ Don't use these for conditional rendering |
| 129 | import { SignedIn, SignedOut } from '@clerk/clerk-react'; |
| 130 | ``` |
| 131 | |
| 132 | ```typescript |
| 133 | import { SignInButton, UserButton } from "@clerk/clerk-react"; |
| 134 | import { Authenticated, Unauthenticated } from "convex/react"; |
| 135 | |
| 136 | function App() { |
| 137 | return ( |
| 138 | <> |
| 139 | <Authenticated> |
| 140 | <UserButton /> |
| 141 | <Content /> |
| 142 | </Authenticated> |
| 143 | <Unauthenticated> |
| 144 | <SignInButton /> |
| 145 | </Unauthenticated> |
| 146 | </> |
| 147 | ); |
| 148 | } |
| 149 | ``` |
| 150 | |
| 151 | ## Clerk Webhooks for User Sync |
| 152 | |
| 153 | See [WEBHOOKS.md](references/WEBHOOKS.md) for complete implementation. |
| 154 | |
| 155 | **Setup in Clerk Dashboard:** |
| 156 | |
| 157 | 1. Webhooks > Add Endpoint |
| 158 | 2. URL: `https://your-deployment.convex.site/clerk-users-webhook` |
| 159 | 3. Events: Select all `user.*` events |
| 160 | 4. Copy Signing Secret → Convex Dashboard env vars as `CLERK_WEBHOOK_SECRET` |
| 161 | |
| 162 | ## Accessing User Info |
| 163 | |
| 164 | ### Client-side (Clerk SDK) |
| 165 | |
| 166 | ```typescript |
| 167 | import { useUser } from "@clerk/clerk-react"; |
| 168 | |
| 169 | function Profile() { |
| 170 | const { user } = useUser(); |
| 171 | return <span>Hello, {user?.fullName}</span>; |
| 172 | } |
| 173 | ``` |
| 174 | |
| 175 | ### Server-side (Convex functions) |
| 176 | |
| 177 | ```typescript |
| 178 | export const myQuery = query({ |
| 179 | handler: async (ctx) => { |
| 180 | const identity = await ctx.auth.getUserIdentity(); |
| 181 | // identity.name, identity.email, etc. |
| 182 | // Fields depend on Clerk JWT template claims config |
| 183 | } |
| 184 | }); |
| 185 | ``` |
| 186 | |
| 187 | ## Dev vs Prod Configuration |
| 188 | |
| 189 | | Environment | Publishable Key | Issuer Domain | |
| 190 | | ----------- | --------------- | ----------------------------------------- | |
| 191 | | Development | `pk_test_...` | `https://verb-noun-00.clerk.accounts.dev` | |
| 192 | | Production | `pk_live_...` | `https://clerk.your-domain.com` | |
| 193 | |
| 194 | Set different values in Convex Dashboard for dev vs prod deployments. |
| 195 | |
| 196 | ## Clerk-Specific Troubleshooting |
| 197 | |
| 198 | | Issue |