Ability to pass additional data to auth context.
How can we pass additional data from say sessions claims to auth context. With Clerk in other scenarios like prisma/trpc, we used to be able to store things we frequently access on the userProfile in session claims, and are able to pass this along to trpc context, and get access to it in all routes. But with convex, no matter what I add to the session claims, it seems everything is striped, except what's defined on the UserIdentity interface. The problem with this, is that it means for every table in our db, if there is any association to a userProfile, we would always have to first fetch the userProfile by it's authUserId(subject/tokenIdentifier), then use the _id field value of the returned userProfile to make any associations.
Current work around is to either store the userProfileId on the client context, and pass that along with every query or mutation, or what we are instead doing is using authUserId as the main relationship key everywhere.
The ideal scenario is what we had before, where when a user is created, we store the userProfileId is Clerk's privateMetadata or publicMetadata, then pass that along with session claims, and use some of the values from session claims to construct the user identity. So now, every authenticated call already has the db userProfileId, role, userType, etc. And we can go about using it in all queries and mutations without first reaching to the db to fetch the userProfile by the Clerk authUserId for every single request.
Would love any suggestions on what you all are doing instead. Or if I am missing something.
Thank you.
16 Replies
Good questions. I would indeed suggest fetching this from the database for each request using the subject/tokenIdentifier. Ultimately what you can store and modify in the database is way more flexible and powerful. The lookup is incredibly fast and cheap. It also allows you to have transactional behavior - changing a user's role will immediately change their permissions without them refreshing their auth token from Clerk or being on the same device.
One challenge is if you rely on storing everything in Clerk and you don't have it in the Convex db. There's a template repo that syncs the state into the convex db. check out the http.ts module in particular.
To make fetching the user more convenient, you can use something like custom functions to fetch the user on every request
Does that make sense?
Templates
The backend application platform with everything you need to build your product.
GitHub
convex-clerk-users-table/convex/http.ts at main · thomasballinger/c...
Contribute to thomasballinger/convex-clerk-users-table development by creating an account on GitHub.
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...
Thanks for the quick response Ian. We already use Clerk webhooks to update parts of the userProfile where needed. But in our case, we particularly want to not fetch the userProfile on every request. Our app is a health platform with multiple sides, patients, providers, coach, and a native app. And after a few experiments, the not fetching is what we found works best and relying on the clerk session as the source of truth for basic information we need from the user. We very rarely need access to other user profile information. And only fetch it on an as need basis. Would be great if there was a way to augment the auth context, even if that means we have to do a bit of work.
The fields we let through are OpenID compliant fields. Some folks have had luck putting fields into unused ones, such as putting an organization id in as the "gender". e.g.
"gender": "{{user.org_id}}"
I've noted your feedback - you're not the only one who wants more flexible auth data and it's good to hear how it's used so we can improve it. Thanks for the feedback!
we particularly want to not fetch the userProfile on every requestSo I understand better, the requests here are Convex query / mutations / actions? Or other types of requests, like to nextjs or something? And is the concern one of performance, convenience, code organization, compliance, or otherwise? Is there a template I could build that would be a helpful guide to building this sort of role-based authentication?
Thanks Ian. I thought about putting it on a seperate field. And while its weird, it would work. Let me go over with my team.
Thanks again.
I'm looking into this and it seems like I might have to also hijack another field. With something like Hasura, you can put x-hasura-role, for example, in the claims object and put whatever you want there. I spent a while wondering why my "role" field I added to my JWT claim wasn't coming over, which is something I store in my metadata. I would definitely like to see the ability to customize this somehow.
Hello is there any update on this? @ian
@Michal Srb has been looking at supporting custom fields recently with his work on auth generally but there hasn't been anything released. Current guidance is to sync that data to Convex through webhooks or repurpose an existing field. The rust library we leverage is pretty pedantic unfortunately and we've been hesitant to fork more auth primitives than we need to
Custom claims are now supported with
convex@1.14.0
just in time! Thanks!
now I just have to figure out why my
{{org.id}}
claim isn't coming through. Seems to be a clerk related issue though since the value ends up being null despite me having a org on the userAwesome. Thank you
@Michal Srb Is there a guide for this? I see for clerk in the doc. But what if we want the auth session to include values from the user db.
Like with next auth, where we want the session to include additional values from the user.
pinging @sshader who's been digging more into auth lately
Are you asking about how to use custom claims / access custom metadata with Convex Auth? Or some other auth provider?
For Convex Auth, I believe you'd just want to store any custom data you want in the
users
table and add a createOrUpdateUser
to pull any relevant information out of the profile and write it to the users
document
https://labs.convex.dev/auth/setup/schema#customizing-the-users-table
https://labs.convex.dev/auth/config/oauth#retrieving-other-profile-information
https://labs.convex.dev/auth/advanced#controlling-user-creation-and-account-linking-behavior
https://labs.convex.dev/auth/api_reference/server#callbackscreateorupdateuserserver - Convex Auth
Authentication library for your Convex backend
OAuth - Convex Auth
Authentication library for your Convex backend
Customizing Schema - Convex Auth
Authentication library for your Convex backend
Advanced: Details - Convex Auth
Authentication library for your Convex backend
Hi @sshader , sorry. Just seeing this. And typically come to discord when I have a question.
I am talking about adding custom properties directly to the auth object, as shown in the screenshot. Previously with clerk, we woud add custom properties via their session claim. Things we use frequently, like isOnboarded, subscription info, etc. Where we don't have to fetch the user, and read this directly from the auth session
I believe this is: https://github.com/get-convex/convex-auth/issues/25
GitHub
Allow storing information in the JWT · Issue #25 · get-convex/conve...
Some people might be more familiar with retrieving information from the JWT instead of from the DB. The client would provide the equivalent of Clerk's useUser hook, and on the backend await ctx...
Thanks @Michal Srb . Yes. That's exactly what I am referring to. Especially the comment on using values in middleware, without hitting the db for every single user to your site
Storing quick values like isOnboarded, org things, etc, where you can intercept the route from the middleware. Since this is on the server, it would require next to call convex db, and you can't rely on caching here really. That call will still be made each and every single time. And while convex can respond with the cached value, it is still an unnecessary communication between the nextjs host and convex server, even if in nano seconds, that does not need to happen.