zor
zor7mo ago

Clerk authenticates, Convex do user management and features. Convex stores user data

Objective Minimal Clerk Side: Use Clerk primarily for user authentication. Convex for User Data Management: Store and manage all user-related data within Convex. Key Steps Set Up Convex Schema: Define the user schema in Convex to store necessary user data fields. Use Convex for Data Operations: Implement queries and mutations in Convex to manage user data, ensuring that all relevant user information is stored in Convex so I can process. I won't create custom flows with Clerk but I will build custom flows with my own database(fetch from my own DB, since storing the same data with Clerk which means similar thing) and backend but Clerk securely handles authentication E.g. I won't use useUser(); from Clerk but useMyDatabaseUser(); In the philosophy of store minimal in Clerk. Isn't it better to manage such features¹ in Convex rather than Clerk? ¹:
last_sign_in_at: number | null;
last_active_at: number | null;
password_last_updated_at: number | null;
two_factor_enabled: boolean;
external_accounts: ExternalAccountJSON[];
last_sign_in_at: number | null;
last_active_at: number | null;
password_last_updated_at: number | null;
two_factor_enabled: boolean;
external_accounts: ExternalAccountJSON[];
E.g. features² probably something we wouldn't have in Convex and Clerk has some characteristics on them ²:
lockout_expires_in_seconds: number | null;
verification_attempts_remaining: number | null;
external_accounts: ExternalAccountJSON[];
banned: boolean;
locked: boolean;

lockout_expires_in_seconds: number | null;
verification_attempts_remaining: number | null;
external_accounts: ExternalAccountJSON[];
banned: boolean;
locked: boolean;

Feel free to share some insights! My approach is copying the UserJSON to my database. Manage the user data from my databases User object and sync when it changes So I will never use useUser() and as I said, useMyDatabaseUser()
14 Replies
zor
zorOP7mo ago
Clerk support has replied me as this:
Could you tell me more about why you feel so strongly about building all your own UI? This is the biggest sticking point - I hear that you want to use an auth service because you can trust it's secure, and that's valid. However, many folks don't quite realize just how much effort it is to build good authentication UIs. We have a team of full time engineers working on our UIs and have been working on them for years. The number of different options you need to accommodate, especially as your app grows, is astonishing.

For example, perhaps you start with email/password login. Easy enough, you can make a form for that pretty quick. But now you want to also add the ablility for your users to use a phone number in addition to an email. Then you want to make sure emails or phone numbers are verified before they make the account to combat spam. Now your app is more successful and you are getting a bunch of bot accounts being made, so you need bot detection and bot signup prevention. And also some people are using dumb passwords and getting their accounts hacked and are complaining about it, so now you need password rules and validation in your sign up logic. And then you want to add the ability for folks to sign in with a facebook account. But some facebook accounts don't have an associated email, just a phone number, so when they come back you still want to collect an email for the account to be valid, so you need to make sure when the oauth response returns, you have an additional form stage before account creation is finalized that collects this.
Could you tell me more about why you feel so strongly about building all your own UI? This is the biggest sticking point - I hear that you want to use an auth service because you can trust it's secure, and that's valid. However, many folks don't quite realize just how much effort it is to build good authentication UIs. We have a team of full time engineers working on our UIs and have been working on them for years. The number of different options you need to accommodate, especially as your app grows, is astonishing.

For example, perhaps you start with email/password login. Easy enough, you can make a form for that pretty quick. But now you want to also add the ablility for your users to use a phone number in addition to an email. Then you want to make sure emails or phone numbers are verified before they make the account to combat spam. Now your app is more successful and you are getting a bunch of bot accounts being made, so you need bot detection and bot signup prevention. And also some people are using dumb passwords and getting their accounts hacked and are complaining about it, so now you need password rules and validation in your sign up logic. And then you want to add the ability for folks to sign in with a facebook account. But some facebook accounts don't have an associated email, just a phone number, so when they come back you still want to collect an email for the account to be valid, so you need to make sure when the oauth response returns, you have an additional form stage before account creation is finalized that collects this.
When building yourself, you need to manually add every one of these features, which is very time consuming. Also, any little mistakes or bugs or edge cases in your own logic will result in people not being able to sign up, getting locked out of their accounts, etc, which loses you users.
If you use Clerk's UI, everything works, no matter how crazy your setup is, and it scales up indefinitely, and anything else we add to the platform is automatically supported out of the box for free
If you want to customize the way the UI looks and works to fit your app, there are a bunch of ways to do this from the appearance prop (https://clerk.com/docs/components/customization/overview#appearance-prop) to clerk elements (https://clerk.com/changelog/2024-05-02-elements-beta). If you really want to toe the line, you could dip into custom flows too (https://clerk.com/docs/custom-flows/overview), but we don't recommend this unless there's no other option bc its a lot more work.
If you were able to let go of the requirement to build all your own UI and store all of the Clerk data in convex, everything becomes much easier for you when building your app, and the flow of how you use the tool becomes a lot more straightforward
I do not think that you need to have Clerk's user data in convex in order to be "safe" or to make your app work, at any time. I do fully understand why you feel this way, and this is a line of questioning we get reasonably often because Clerk's auth architecture is fairly unique. But it does work, and if you're gonna use Clerk, you will get the most value leaning into it
When building yourself, you need to manually add every one of these features, which is very time consuming. Also, any little mistakes or bugs or edge cases in your own logic will result in people not being able to sign up, getting locked out of their accounts, etc, which loses you users.
If you use Clerk's UI, everything works, no matter how crazy your setup is, and it scales up indefinitely, and anything else we add to the platform is automatically supported out of the box for free
If you want to customize the way the UI looks and works to fit your app, there are a bunch of ways to do this from the appearance prop (https://clerk.com/docs/components/customization/overview#appearance-prop) to clerk elements (https://clerk.com/changelog/2024-05-02-elements-beta). If you really want to toe the line, you could dip into custom flows too (https://clerk.com/docs/custom-flows/overview), but we don't recommend this unless there's no other option bc its a lot more work.
If you were able to let go of the requirement to build all your own UI and store all of the Clerk data in convex, everything becomes much easier for you when building your app, and the flow of how you use the tool becomes a lot more straightforward
I do not think that you need to have Clerk's user data in convex in order to be "safe" or to make your app work, at any time. I do fully understand why you feel this way, and this is a line of questioning we get reasonably often because Clerk's auth architecture is fairly unique. But it does work, and if you're gonna use Clerk, you will get the most value leaning into it
This message has made me doubt if I'm doing something wrong
erquhart
erquhart7mo ago
You're not, Clerk wants people to use Clerk all the way, that's all. A lot of folks don't want all of their data in Convex, but have to keep user source of truth in a separate service. I think the message here is Clerk isn't going to help you use Clerk less. Which is fair. But the community here will help 👍 Syncing user data when it changes is what most folks in your position are doing. Have you run into issues? Or any specific concerns? I'm also assuming you know about Convex Auth and are choosing to go with Clerk for features that Convex Auth doesn't provide.
zor
zorOP7mo ago
It wasn't a thing when I started I guess and I don't know much about Convex Auth. First of all, thank you so much. You can't imagine how helpful any response is. I currently have webhook sync Clerk -> Convex. I don't know how much should Convex include or should be whole UserJSON of Clerk. It was only externalId and firstName I guess. Now I think I should have majority of the UserJSON that Clerk sends to Convex. Then can manage the user primarily on Convex rather than Clerk. But the thing is, it will keep handling MFA, etc. and it has some features such as banned, lockout etc. well, how will it work together what should be in Convex, and Clerk. Will it work etc. I'm very confused and my ideas very inconsistent So sorry if my words doesn't make any sense Or will I manage users password on Convex or Clerk? What about email. Let's say I build device revocation, how will Convex tell Clerk to revocate the device? https://clerk.com/docs/references/javascript/user/user
erquhart
erquhart7mo ago
In that case: - sounds like you want functionality that's beyond what Convex Auth has, so staying with Clerk makes sense - you do want to let Clerk handle passwords, don't store them in Convex at all, same for any other auth artifact (tokens, etc). If you're using Clerk, use it all the way for the authentication process specifically. - however, any user data that you want to access in your app, including on the backend, syncing Clerk data back to Convex via webhook is the way to go - have not looked into revocation at all, does Clerk support that?
zor
zorOP7mo ago
It does if purchased the required extension As you know, Clerk works well with their own UI and I want the same functioning but on my database and without its security intelligence Understood I should not store password. Well, won't I need to access attributes such as phoneNumber if verified: boolean, or 2FA enabled(e.g. user menu gives alert to enable it for better security) It is just too comprehensive and if I think about an architecture, I think about some scenarios and I don't know how should it be. Convex team has told me I would most likely want some of the features to be on Convex side such as enhanced roles and some other And my objective is to have the same level of functionality and have at least majority on Convex and be able to process directly from Convex
Those features all sound nice, and a good use of Clerk IMO. The one that stands out to me as better in Convex state is "Enhanced roles" - I bet you'll want your "who has access to what and which organization are they in" to be in your Convex DB, not Clerk metadata. Make sense? You would be building all of the device tracking, revocation, etc yourself. Convex doesn't have any out-of-the-box features for these things, so if you want them soon I'd start with Clerk, and if you later want to build a more custom version of them you can build them from scratch later (or maybe find some library / component that works with Convex) re: performance, your user queries will be alongside your other Convex data, so it'll be faster in that regard. However, there is a bit of a waterfall of requests: Clerk says you're signed in and the client gets the token Convex sends the token up, then calls something like storeUser and a user is created / verified that it exists Your functions that depend on auth run
I sent these quotes if it helps to understand the benefits and motivation
erquhart
erquhart7mo ago
Yeah that all makes sense. I agree w/ the quote, authorization/roles I would keep in your app and stored in Convex, no need to put that in Clerk. As far as accessing phone number, verified, etc. that's part of the state that you would keep synced between Clerk and Convex.
zor
zorOP7mo ago
So I should keep that in my Convex user table. Then how much from the UserJSON of Clerk? last_active_at: number | null; banned: boolean; Should I ignore these better-to-be-in-Convex Clerk's attributes or keep them in sync(likely)? Another example, Clerk has external_accounts: ExternalAccountJSON[];
export interface ExternalAccountJSON extends ClerkResourceJSON {
object: typeof ObjectType.ExternalAccount;
provider: string;
identification_id: string;
provider_user_id: string;
approved_scopes: string;
email_address: string;
first_name: string;
last_name: string;
image_url?: string;
username: string | null;
public_metadata?: Record<string, unknown> | null;
label: string | null;
verification: VerificationJSON | null;
}
export interface ExternalAccountJSON extends ClerkResourceJSON {
object: typeof ObjectType.ExternalAccount;
provider: string;
identification_id: string;
provider_user_id: string;
approved_scopes: string;
email_address: string;
first_name: string;
last_name: string;
image_url?: string;
username: string | null;
public_metadata?: Record<string, unknown> | null;
label: string | null;
verification: VerificationJSON | null;
}
Should I have this on my Convex user table as well? I thought about dual-sided sync as well. I would sync Clerk ➡️ Convex with webhook which I already have that setup and for the Convex ➡️ Clerk I would do API call to Clerk(I don't know how and if possible). Or do I use Clerk's functions such as createBackupCode() from Clerk to update it on Clerk as well? If that's something I probably won't need and I shouldn't be worried about. I am okay to not access it or hybridly use Clerk functions on these specific cases if I should use I hope not bothering you. Thank you really so much 🫶
erquhart
erquhart7mo ago
No bother, this is what support forum is for 👍 Two approaches: 1. sync only the data you need to access within your application logic 2. sync everything just in case you need it My own approach, for whatever it's worth, is to shape my own user data (in Convex) independent of Clerk's (or any provider's) chosen format. So I have fields in my users table that represent what I need and want to store for my user, regardless of how I'm doing auth. Some of those values I get from Clerk, some I get through my own UI and forms. Are you using their components in your UI by chance?
zor
zorOP7mo ago
I am using Clerk's UIs completely at the moment. The goal is using custom UIs and custom user settings
erquhart
erquhart7mo ago
Eventually or you're working on replacing them now Either way, though, syncing back to Convex and using the data in Convex will work for you I don't know if you've experienced that not being the case in your usage
zor
zorOP7mo ago
Sounds good. I like the 1. Maybe I won't need whole user as same as what I receive from Clerk. In the user settings, I will update the Convex data and the sync system will send the updated convex user to Clerk anyways. No matter what, Convex data is the single source of truth and hopefully there won't occur weird bugs such as, in the user settings, user is alerted to verify phone number, user verifies on Clerk and Convex still thinks it is not verified etc. but it is pretty unlikely I guess and if occurs, there will be a simple way to fix it like a normal problem. Well, how do Convex tell Clerk to do something like revocating the user(I guess basically it is ending a session and I'd get the session Id by Convex(which gets from the JWT token that Clerk gives) and end with Clerk's function)and how do Convex send the updated user, maybe I got dumb with the overwhelm but in the case user table attribute names are going to be different than Clerk, at least not identical. How the Clerk will get sync with the user data with different attribute names? Oh makes sense Probably making the table names identical will just work to replace(upsert)?
erquhart
erquhart7mo ago
Not sure what you mean here, can you explain
zor
zorOP7mo ago
I just realized it was an unnecessary concern. For example, you see the phoneNumbers array there in the Convex database. And assume phoneNumbers attribute is named arrayPhoneNumbers in Clerk. I asked how Clerk can understand received phoneNumbers attribute value should be replaced to arrayPhoneNumbers but probably there is not such reason so the question was wrong. I believe I should just keep the names same.
No description
erquhart
erquhart7mo ago
Yeah that works

Did you find this page helpful?