ConvexProvider auth hook
An idea, maybe <ConvexProviderWithClerk /> could take an optional function as a prop that defines how to copy Clerk data into Convex? Kind of like folding my useStoreUser() hook into <ConvexProviderWithClerk />. Oh well, just an idea.
7 Replies
I really like this idea. Michal is still out of the country for a day, but wanted to thank you for sharing. We've been thinking about this problem of needing all these loading states:
1. when the auth provider says you're logged in
2. when convex has passed your credentials to the backend, so your query / mutations can access it
3. when your code has created a user, so query/ mutations will be able to fetch a user successfully.
Passing in an async function to do (3) would let the Convex <Authenticated /> component let you know when all 3 are done. Or we could use something like a helper hook that returns components to use when your storeUser fn has finished.
The challenge comes in when you're trying to decide whether to show a "Log In" button - you need to convey that the user has logged in according to Clerk, but that you're waiting for Convex / storeUser to finish. Otherwise you end up with a problem like RJ's in #support-community I think. Which I think is the intent of the "loading" state, though I haven't been working directly on this
Hey @ian it was great to meet you at Reactathon! I am still excited about Convex, but the more I think about this, and the more I tinker with Convex, I believe auth should be decoupled from Convex. I like the whole database concept but it just feels like I am fighting the auth part all the time. I see how you want to make auth easy, but maybe it is just making a bad situation worse? Oversimplifying something can sometimes make it harder to use - unless you have a VERY standard use case (which no one really has, and use cases change over time).
I heard from some some other peeps at Reactathon that Convex is the best attempt so far at building a "database for the frontend", but peoples concern are all the "magic" provided which makes it hard to understand what's going on and even more so to migrate away if needed.
I think it would be great if auth was optional and if you provide a drop in package for auth that would be awesome. But it should be very OPTIONAL.
tRPC is great in that it has no oppinion on auth, but provides functions where I can do what ever I want. It's like all the good stuff from GraphQL, without the complexity. Convex opportunity is in adding pub/sub to the DB and auto publishing functions to tRPC (and a simple drop in auth package would be cool to help adoption/getting started).
If I only could get access to HTTP headers in the server side DB functions, I could pick up my own cookies and cook up my own auth.
Re: auth, that makes a lot of sense to me. I think it's important to be able to build on Convex with non-standard auth. I agree that auth often gets nuanced - with sign-up codes, email magic links, API keys, etc, you often need another abstraction layer over auth.
I think our current auth offering is indeed targeting the "common" case.
It is technically already possible to do auth without using our built-in auth abstractions, it just requires you to manage that layer within the convex constraints (in particular side-effect-free functions for query/mutation). Some of the primitives that would be required to do it yourself (to be transparent, I haven't actually done this myself yet, so I'm hypothesizing a bit):
1. You'd use http endpoints to handle any oauth flows that require a server callback
2. You'd use an action to fetch data from a 3rd party provider and store the user data in a Convex table.
3. You'd use something like my sessions "middleware" to keep track of a user session to associate with auth. You could store auth & user info in the session.
4. You'd use something like my
useSessionQuery
hook for any query that required auth.
Ultimately the "auth" abstraction provided by Convex "just" passes a JWT up over the websocket / http that is decoded & parsed as an arg to functions that call getUserIdentity()
. It in some ways is just some sugar over passing up that argument yourself, with the optimizations that (1) it's passed up once over the websocket, not with every request, (2) it decodes and validates the JWT with your provided auth keys, and (3) as a convenience, the client refreshes the token right before it's due to expire, but nothing beyond what you could do yourself.
This is motivating me to actually try to build this with some off-the-shelf auth solution that we don't currently support. If I start working on it, I'd love input on what the ideal API would look like for you. We've also heard this from others in the past too, so I think we need a more concrete answer in the near term before we (hopefully) offer a more flexible auth offeringHey, that's awesome! I will explore this a bit more when I have the time. I've also been adding some more thoughts around auth here https://discord.com/channels/1019350475847499849/1106003199896326144
I'm feeling the exact same thing atm and came across this post by searching "without auth" to see what people were thinking haha
If you want a really fun challenge that's hot rn and I've been playing with, try a WebAuthn provider like Passage:
https://passage.1password.com/
Passwordless Authentication Powered by Passkeys | Passage by 1Password
Passage provides passwordless authentication that you can add to your app or website with just a few lines of code. Learn more.
Cool, I'm going to take a look! Thanks for sharing!