npm-packages/docs/docs/auth/database-auth.mdx
import Schema from "!!raw-loader!@site/../demos/users-and-clerk/convex/schema.ts"; import useStoreUserEffectTS from "!!raw-loader!@site/../demos/users-and-clerk/src/useStoreUserEffect.ts"; import useStoreUserEffectJS from "!!raw-loader!@site/../private-demos/snippets/users-and-clerk/useStoreUserEffect.js"; import MessagesTS from "!!raw-loader!@site/../demos/users-and-clerk/convex/messages.ts"; import UsersTS from "!!raw-loader!@site/../demos/users-and-clerk/convex/users.ts"; import App from "!!raw-loader!@site/../private-demos/snippets/src/clerkStoreUserApp.tsx"; import WebhooksSchema from "!!raw-loader!@site/../demos/users-and-clerk-webhooks/convex/schema.ts"; import WebhookMutations from "!!raw-loader!@site/../demos/users-and-clerk-webhooks/convex/users.ts"; import WebhookEndpoint from "!!raw-loader!@site/../demos/users-and-clerk-webhooks/convex/http.ts"; import WebhookMessages from "!!raw-loader!@site/../demos/users-and-clerk-webhooks/convex/messages.ts"; import WebhookHook from "!!raw-loader!@site/../demos/users-and-clerk-webhooks/src/useCurrentUser.ts"; import WebhookClient from "!!raw-loader!@site/../demos/users-and-clerk-webhooks/src/App.tsx";
If you're using Convex Auth the user information is already stored in your database. There's nothing else you need to implement.
You might want to store user information directly in your Convex database, for the following reasons:
There are two ways you can choose from for storing user information in your database (but only the second one allows storing information not contained in the JWT):
ctx.authExample: Convex Authentication with Clerk
You can define a "users" table, optionally with an
index for efficient looking up the
users in the database.
In the examples below we will use the tokenIdentifier from the
ctx.auth.getUserIdentity() to identify the user, but you could use the
subject field (which is usually set to the unique user ID from your auth
provider) or even email, if your authentication provider provides email
verification and you have it enabled.
Which field you use will determine how multiple providers interact, and how hard it will be to migrate to a different provider.
<Snippet source={Schema} snippet="user" title="convex/schema.ts" />This is an example of a mutation that stores the user's name and
tokenIdentifier:
You can call this mutation when the user logs in from a useEffect hook. After
the mutation succeeds you can update local state to reflect that the user has
been stored.
This helper hook that does the job:
<TSAndJSSnippet sourceTS={useStoreUserEffectTS} sourceJS={useStoreUserEffectJS} title="src/useStoreUserEffect.ts" />
You can use this hook in your top-level component. If your queries need the user document to be present, make sure that you only render the components that call them after the user has been stored:
<TSAndJSSnippet sourceTS={App} sourceJS={App} title="src/App.tsx" />In this way the useStoreUserEffect hook replaces the useConvexAuth hook.
Similarly to the store user mutation, you can retrieve the current user's ID, or throw an error if the user hasn't been stored.
Now that you have users stored as documents in your Convex database, you can use their IDs as foreign keys in other documents:
<TSAndJSSnippet
sourceTS={MessagesTS}
sourceJS={MessagesTS}
snippet="load-user"
title="convex/messages.ts"
suffix={ // do something with \user`...
}
});`}
/>
The information about other users can be retrieved via their IDs:
<TSAndJSSnippet
sourceTS={MessagesTS}
sourceJS={MessagesTS}
snippet="use-users"
title="convex/messages.ts"
prefix={import { query } from "./_generated/server"; }
/>
This guide will use Clerk, but Auth0 can be set up similarly via Auth0 Actions.
With this implementation Clerk will call your Convex backend via an HTTP endpoint any time a user signs up, updates or deletes their account.
Example: Convex Authentication with Clerk and Webhooks
On your Clerk dashboard, go to Webhooks, click on + Add Endpoint.
Set Endpoint URL to
https://<your deployment name>.convex.site/clerk-users-webhook (note the
domain ends in .site, not .cloud). You can see your deployment name in
the .env.local file in your project directory, or on your Convex dashboard as
part of the Deployment URL. For example,
the endpoint URL could be:
https://happy-horse-123.convex.site/clerk-users-webhook.
In Message Filtering, select user for all user events (scroll down or use the search input).
Click on Create.
After the endpoint is saved, copy the Signing Secret (on the right side of the
UI), it should start with whsec_. Set it as the value of the
CLERK_WEBHOOK_SECRET environment variable in your Convex
dashboard.
You can define a "users" table, optionally with an
index for efficient looking up the
users in the database.
In the examples below we will use the subject from the
ctx.auth.getUserIdentity() to identify the user, which should be set to the
Clerk user ID.
This is an example of mutations that handle the updates received via the webhook:
<TSAndJSSnippet sourceTS={WebhookMutations} sourceJS={WebhookMutations} title="convex/users.ts" />
There are also a few helpers in this file:
current exposes the user information to the client, which will helps the
client determine whether the webhook already succeededupsertFromClerk will be called when a user signs up or when they update
their accountdeleteFromClerk will be called when a user deletes their account via Clerk
UI from your appgetCurrentUserOrThrow retrieves the currently logged-in user or throws an
errorgetCurrentUser retrieves the currently logged-in user or returns nulluserByExternalId retrieves a user given the Clerk ID, and is used only for
retrieving the current user or when updating an existing user via the webhookThis how the actual HTTP endpoint can be implemented:
<TSAndJSSnippet sourceTS={WebhookEndpoint} sourceJS={WebhookEndpoint} title="convex/http.ts" />
If you deploy your code now and sign in, you should see the user being created in your Convex database.
You can use the helpers defined before to retrieve the current user's document.
Now that you have users stored as documents in your Convex database, you can use their IDs as foreign keys in other documents:
<TSAndJSSnippet sourceTS={WebhookMessages} sourceJS={WebhookMessages} snippet="current-user" title="convex/messages.ts" />
The information about other users can be retrieved via their IDs:
<TSAndJSSnippet sourceTS={MessagesTS} sourceJS={MessagesTS} snippet="use-users" title="convex/messages.ts" />
If you want to use the current user's document in a query, make sure that the user has already been stored. You can do this by explicitly checking for this condition before rendering the components that call the query, or before redirecting to the authenticated portion of your app.
For example you can define a hook that determines the current authentication state of the client, taking into account whether the current user has been stored:
<TSAndJSSnippet sourceTS={WebhookHook} sourceJS={WebhookHook} title="src/useCurrentUser.ts" />
And then you can use it to render the appropriate components:
<TSAndJSSnippet sourceTS={WebhookClient} sourceJS={WebhookClient} snippet="client-blocking" title="src/App.tsx" />