Auth & Access
Wiring Clerk to Convex
Lesson 6 of 7
What you'll learn
- See how Clerk and Convex hand off identity
- Understand the provider nesting that makes it work
- Read the signed-in user inside a Convex function
Authentication has two halves: proving who someone is (Clerk's job) and letting your backend trust that proof (Convex's job). The bridge between them is a signed JWT.
The provider sandwich
On the client, Convex needs Clerk's auth state. So ClerkProvider must wrap ConvexProviderWithClerk:
"use client";
import { ClerkProvider, useAuth } from "@clerk/nextjs";
import { ConvexProviderWithClerk } from "convex/react-clerk";
<ClerkProvider>
<ConvexProviderWithClerk client={convex} useAuth={useAuth}>
{children}
</ConvexProviderWithClerk>
</ClerkProvider>
ConvexProviderWithClerk grabs the current Clerk token and attaches it to every Convex request. On the server side, convex/auth.config.ts tells Convex which issuer to trust:
export default {
providers: [
{ domain: process.env.CLERK_JWT_ISSUER_DOMAIN, applicationID: "convex" },
],
};
Reading identity
Now inside any Convex function, ctx.auth.getUserIdentity() returns the verified claims — or null if the request is anonymous. The subject field is the Clerk user ID:
const identity = await ctx.auth.getUserIdentity();
if (!identity) return []; // not signed in
const userId = identity.subject; // stable Clerk user id
Use the right auth hook
On the client, check auth state with Convex's useConvexAuth() (not Clerk's useAuth()), so your UI reacts to the state Convex actually sees. Gate authenticated queries behind it or the <Authenticated> component.
The exercise models the token handoff: a token is issued, attached to a request, and verified server-side before any data is returned.
Run it. An anonymous request gets nothing; a request carrying a valid token gets the user's data. That single check guards your whole backend.
Identity flows end to end. Last step: deciding what each user is allowed to see.
Sign in to save your progress across devices.