Auth with Clerk and Convex
I'm very new to Convex and am just going through the tutorials and documentation. But from what I've seen so far it really is the missing piece I've been looking for. Really excited to test this fully on some projects.
However, it still seems like there's a little bit of friction when setting up auth. From reading some of the support threads it seems that, if you're using Clerk, then you should almost certainly have a users table with a Clerk ID so that when a user logs in you can grab the Convex user via the Clerk ID and then base all Convex queries and mutations on the Convex user ID (hope I got that right). While there is a template, and docs for how to implement this it feels perhaps a little too manual when creating a new app.
From a very new users perspective, I think that if Convex were to give a bit of attention to this area and make it super easy to add auth so that you almost didn't even have to think about it that would be amazing and you could effectively just spin up a new app with a db (via Convex) and have auth pretty much just work and you could just start to query users in the Convex table. I think if this was the case it would make Convex even better (it's pretty incredible already!).
In summary, at the moment I can create a Next.js app with Convex really easily, so I have a deployment and db support (yay!). But setting up auth still takes a little effort. I'd love to be able to spin up a Next.js app, add Convex, and auth (Clerk) and just have it all work as quickly as possible, so I can focus on building the app.
Would love to get feedback from developers who have been using Convex with Clerk for some time to know if this problem effectively disappears once you're super familiar with all the API's etc.
38 Replies
You're right about needing to exchange the Clerk id for a Convex id. It's not a hassle though, I do it in a single, very small function that's reused in each query/mutation/action. A query in my app will generally start like this:
I was using Clerk before I migrated to Convex, so authentication was very quick to set up and move on. Authorization was more involved because it's tied to how your app works. There are solutions, articles, and tools for this, and more coming soon. But I can say that, once set up, auth and authz is not a problem I've had to think about since.
Thanks for that. For me, auth is the missing piece to get right and then I can fully focus on the app itself. Just need more practice and experimentation I guess.
I'm assuming you've already seen the Clerk guide in the Convex docs - is there anything in particular that feels unsure going in?
I started with the Next.js quick start guide and got Clerk set up but not the bit with adding a users table in Convex and having that updated by Clerk via a webhook.
I guess I'm confused a little by what the optimum set up is?
Is it having Clerk update the users table via the webhook? Is that the recommended standard approach, because the Convex docs don't mention webhooks in the Clerk auth.
https://docs.convex.dev/auth/clerk
Convex Clerk | Convex Developer Hub
Clerk is an authentication platform providing login via
Maybe both approaches are perfectly valid but is a source of confusion at the moment.
I also saw these articles:
https://stack.convex.dev/row-level-security
https://stack.convex.dev/custom-functions
But is a little over my head right now. 🙂
Row Level Security: Wrappers as "Middleware"
Implementing row-level security on Convex as a library. Wrap access to the database with access checks written in plain old JS / TS.
Customizing serverless functions without middleware
Re-use code and centralize request handler definitions with discoverability and type safety and without the indirection of middleware or nesting of wr...
I'm not sure if the webhooks approach is in the docs, but yeah it's the recommended best practice and probably should be covered in docs.
Clerk supports calling a webhook whenever user data changes. You would set up an http action in Convex to accept the webhook and record any relevant changes.
HTTP Actions | Convex Developer Hub
HTTP actions allow you to build an HTTP API right in Convex!
So, to clarify. The approach you use yourself is to use a Clerk webhook to update the users table in Convex (via http action) whenever a user logs in/out?
And then inside the app you grab the Convex users ID as early as possible from the user session and use that only in your app rather than query Clerk?
If so, have you found this a robust, stable approach? i.e. no issues with syncronisation?
The webhook fires when user data changes only, so not that often per user. I haven't actually gone fully live yet, so I can't speak to issues at scale. But I will say to keep in mind that the Clerk id is what you're really relying in, and that doesn't change. I'm currently setup to update the user email if Clerk has something different than Convex when they authenticate, to be doubly sure things are synced. And I'm avoiding keeping any other data with Clerk beyond email - anything else the user enters directly to my app and is stored in the Convex table only.
How do you avoid keeping other data in Clerk, doesn't that store name etc. by default too?
Probably depends on how you're using it - I use email/password only, and I'm not using their components. But even if you use OAuth and Clerk automatically gets the user's name, etc. via the authorizing service, you could very well just use that info initially (or not at all), store it in your user table, and allow your user data to be separate from Clerk's moving forward. If you're using their components, I'm not sure whether they make it more difficult to do this.
It does look like you can turn off first name, last name, username, etc in the Clerk dashboard, even if you're using their components.
Hmm, see what I mean. There are quite a few considerations when setting up auth. On the surface it seems quite simple but in practice, for new Convex users, is a little challenging.
No doubt it will eventually make sense but is a bit of a blocker to get this right first before focusing on app specific features.
If Convex could streamline this and make it super easy to implement that would be a huge win for the platform IMO.
Oh I totally agree! I just think that the complexity you're referring to also exists in any other solution you might use.
Exactly, which is why if Convex could make this a smoother process it would be awesome for all concerned.
For sure. They've actually talked a lot about it - I think their upcoming Convex Components will help too: https://stack.convex.dev/the-software-defined-database#introducing-convex-components
Looks pretty sweet, thanks for the link.
What does your users table schema look like by the way. In the Convex/Clerk template they just store the whole Clerk object pretty much.
Just storing the email and some application stuff. Default fields is stuff like
updatedAt
.Ok, so you just store the Clerk ID and not the whole Clerk object?
Yep, that's all
I do keep the email synced with Clerk as well
I just forked the Clerk template example app and got it working. What I like about storing the Clerk object as they do in the template is that if the Clerk info (any field) is changed on Clerk it filters through and updates it on Convex.
Totally - there's no problem with doing that
Phew. 😂
In Clerk when creating the webhook, what events do you subscribe too? I just checked everything but maybe just the three user events for
user.created
, user.deleted
, user.updated
?I haven't actually set up webhooks for this, I just update when the user logs in currently, but I'll probably implement webhooks soon. I would run mutations on the spot when the user is created, so I'd personally just have webhooks for update and delete.
I wouldn't want my user sign up experience to be at the mercy of a webhook lol
But you could also just create the Convex user with minimal data and get the rest through the
user.created
webhook, so there's definitely a case for using it.
So much of this is preference, seriously.Yes, mind boggling the set up variations. Probably why it's so confusing initially.
I personally sleep better when I know I'm doing things The Right Way, so I had a lot of the feels that you do coming in.
Glad it's not just me!
I think for the sake of simplicity I'll follow along with the Clerk template way of handling auth until I have more confidence to customize things.
At least it gives me a basis for moving forward with further exploration.
You can go really far before you truly need to nail things down. My encouragement would be to give as little time as you can to concerns of scale until you actually need to worry about them, just cut straight to the good stuff (your actual app).
I'm hopefully fairly close to doing that, thanks for the insights. Much appreciated.
I think the only query I have right now is the webhook domain URL. I can't ind a reference to that anywhere.
In the Clerk template readme:
https://github.com/thomasballinger/convex-clerk-users-table
It says:
Set the url to something like this (replace ardent-mammoth-732 with your own) https://ardent-mammoth-732.convex.site/clerk-users-webhook
Is the convex.site arbitrary, or is that fixed and needs to be the same for every Convex webhook you might create.
This might be in the docs, not dug that deep yet.
GitHub
GitHub - thomasballinger/convex-clerk-users-table
Contribute to thomasballinger/convex-clerk-users-table development by creating an account on GitHub.
It's in the HTTP actions doc: https://docs.convex.dev/functions/http-actions
HTTP Actions | Convex Developer Hub
HTTP actions allow you to build an HTTP API right in Convex!
3rd line:
HTTP actions are exposed at https://<your deployment name>.convex.site (e.g. https://happy-animal-123.convex.site).Rather than thinking in terms of "Convex webhooks", think in terms of the
http.(js|ts)
file being an HTTP API into your Convex deployment. You can create whatever endpoints you like there, and access them through http requests however you want - including via webhook.Ah yes, duh. Thanks. Bit too much info to absorb in one session. 😂
lol np, it's not super obvious if you don't know
Happy to help if you have any other thoughts/questions
Great! Thanks so much.
can you point me to that part in the template? I assume it means the template README tells you to setup webhooks that do this right?
We'll work on simplifying the auth setup soon. There's so much to do! 😇
@hyperzone I'm actually not seeing it either, they insert just two fields: https://github.com/get-convex/convex-demos/blob/d4b6b97ce839ade881109653a6ffe8ea5ff4d082/users-and-clerk/convex/users.ts#L37-L41
I may be looking at a different template than the one @David used.
Ah! Found it. It's linked in a readme, it's an example for users table syncing specifically: https://github.com/thomasballinger/convex-clerk-users-table/
Here's the code in question: https://github.com/thomasballinger/convex-clerk-users-table/blob/a78a9083cede8b834a80e1c6c2d189ee08b490c9/convex/http.ts#L26-L38
thanks!