Custom auth
Hi everyone,
I am trying to understand how auth works in convex.
I want to implement custom oauth and not use clerk or auth0.
What I usually do is I have 2 endpoints per providers.
For example, /oauth/google that redirects users to the google login page, and another endpoint /oauth/google/callback that get the user profile and insert it into the database.
Then I create a custom JWT, redirect the user to https://frontend/?access_token=***, extract the access_token on the frontend (SPA), store it in local storage, then include it in every requests I make to my API.
I know I can create endpoints with http actions (for oauth redirect and callback), store users in the database and generare JWT but is there a way to setup the convex javascript client to send my own JWT on every requests?
I tried to use client.setAuth() but it says provider not recognized.
My goal is to be able to use convex built in ctx.auth in server functions so that I don't have to fetch the user manually at the beginning of every function.
46 Replies
Thanks for posting in <#1088161997662724167>.
Reminder: If you have a Convex Pro account, use the Convex Dashboard to file support tickets.
- Provide context: What are you trying to achieve, what is the end-user interaction, what are you seeing? (full error message, command output, etc.)
- Use search.convex.dev to search Docs, Stack, and Discord all at once.
- Additionally, you can post your questions in the Convex Community's <#1228095053885476985> channel to receive a response from AI.
- Avoid tagging staff unless specifically instructed.
Thank you!
You might want to check out https://labs.convex.dev/auth (either to use directly, or to use as a reference implementation).
Auth with Convex works with JWTs -- you generally provide the client (e.g. React client, JS client, but lmk if you're using a different client) with a
fetchToken
function that fetches a JWT client-side (e.g. reading from local storage, making a call to something like clerk / auth0). And then the Convex backend will validate that this token is valid according to the OIDC spec using your auth.config.ts
.
As long as your implementation of fetchToken
respects the forceRefresh
parameter (i.e. it returns a new JWT when forceRefresh
is true), the Convex client handles refetching the token and making sure the backend always has an up to date token.
If you're seeing errors like "no provider found" or that the token is not valid, I'd check that the token matches up with you auth.config.ts
(specifically the iss
field should match a domain in your auth.config.ts
) -- check out https://docs.convex.dev/auth/debug#step-3-check-that-backend-configuration-matches-frontend-configurationConvex Auth - Convex Auth
Authentication library for your Convex backend
Debugging Authentication | Convex Developer Hub
You have followed one of our authentication guides but something is not working.
Thank you for your help.
I looked at convex auth source code (which is for react only?) and it looks like that's what I am trying to achieve, but with svelte.
I am still confused about setAuth and auth.config.js.
It looks like setAuth requires an IdToken and not an access token. And it looks like convex backend will try to validate the IdToken based on the provider in the "iss" and will try to reach well-known/jkws.json?
Does that mean if I want to use my own token in setAuth() I need to implement my own oauth backend (with the well-known/jwks.json endpoint)?
Yes, sounds like you have the right understanding of this.
If it is your goal to implement auth without using Clerk or Auth0, then using Convex Auth directly seems like the way to go. It implements well-known/jkws.json etc. for you, as an OAuth OpenID Connect ID provider.
If you don't want to use the Convex Auth library, but you want to mint your own JWTs that can be used with setAuth with Convex, then yes that's exactly right, setAuth requires an identity token. So yes, if you want to use your own token in setAuth you need to implement your own OAuth backend. The Convex Auth library does this for you, so it's a good place to start.
(which is for react only?)Yeah, I count about 600 liners of React code in https://github.com/get-convex/convex-auth/tree/main/src/react so if you want to do this with Svelte, that's the part you'll need to reimplement.
GitHub
convex-auth/src/react at main · get-convex/convex-auth
Library for built-in auth. Contribute to get-convex/convex-auth development by creating an account on GitHub.
Thanks! I just realized I can still use convex-auth backend part and implement only the svelte front end part.
Hello @Tom and @sshader . I am doing almost the exact same thing with @Spioune . Here is my context. I have already setup a fully custom Oauth flow with google, fully barebones no library just nextjs and ironsession to save the session info. So to make convex aware of my currently signed in user I am using the below pattern
that almost works. I get a valid token in my own unique ways and return it. but i get the error message
Failed to authenticate: "No auth provider found matching the given token", check your server auth config
okay. now with my many searches online i think I am supposed to follow the link shared by @sshader that guides setting up with convex auth but in my following it seems convex is kind of tied to auth.js
especially the part of getting the Google provider.
Bro I cannot stress enough how much I want to avoid that library. I do not want to see it at all in my codebase. How can I avoid the error without having to install AuthJS.
Here is my current auth.config.ts
The Convex Auth library is built around sharing code with Auth.js. It sounds like you don't want to use the Convex Auth library (npm library
@convex-dev/auth
), but instead you want to use the built in auth support that is part of Convex (npm library convex
)
That allows you to use your own JWT if you add the right info to convex/auth.config.ts
. See https://docs.convex.dev/auth/advanced/custom-auth.Hi @AmohPrince
I came to the conclusion that no matter how "custom" you want to implement your auth, the only thing you cannot control is how convex backend validates the token you pass to setAuth().
It does this by looking at the "iss" field inside the token (it needs to be a JWT, so your iron session token won't work), looks at your auth.conf.ts to find the matching iss then calls the /.well-known/jwks.json endpoint to get the public keys to verify the token.
So if you want to be 100% custom auth, you will need to implement this (see OpenID spec).
I didn't go this far and I didn't use convex for a long time so my memory is a bit rusty.
But that's what I remember.
@Tom it would be nice if we could have our own token validator on the backend and setAuth would then just be a way to attach any session id to the requests. Then for DX, convex could provide an already made function that does the OICD validation like it is done today.
Thanks @Spioune . I found out a way here https://docs.convex.dev/auth/advanced/custom-auth it worked for me
Custom Auth Integration | Convex Developer Hub
Note: This is an advanced feature! We recommend sticking with the
Hi Everyone!
I'm trying to use custom auth with Convex in a React Native app, but so far, no luck. Here's what I have:
* I retrieve the token from my third-party SDK.
* I’ve added a hook to pass the token to ConvexProviderWithAuth, and I’ve set the Convex client to use verbose: true.
* I checked the token on jwt.io, and it includes all the required fields listed in https://docs.convex.dev/auth/debug
* I’ve configured auth-config on the backend, and I know it’s working—if I change the domain, I get a "provider not found" error instead.
* I also have another working Clerk provider set up for the web version and another app flavor.
* When the token is expired, I can see it in the React Native logs (WebSocket authentication error). So the token is being received on backend.
The current error i got is:
DEBUG 2025-04-24T14:01:24.322Z received ws message with type AuthError
DEBUG 2025-04-24T14:01:24.324Z attempting to reauthenticate: Could not verify token claim [v1]
At this point i'm stuck, which claim could be missing?
Debugging Authentication | Convex Developer Hub
You have followed one of our authentication guides but something is not working.
My token's aud claim is an array, that could be the issue? i added only one option to the applicationID config, both are urls
If i set a wrong applicationID the error changes to attempting to reauthenticate: No auth provider found matching the given token [v1]
setting up cusotm auth, I"m routing via ngrok locally and I"M getting {
"code": "NoAuthProvider",
"message": "No auth provider found matching the given token"
}
the audience, applicationId are of the same value. the issuer's are also the same.
I"m using ES256, don't know what to do anymore. I'm I missing something?
don't know what happened diffetnly but I"m getting
{
"code": "InvalidAuthHeader",
"message": "Could not decode token"
}
Can you decode your jwt with jwt.io and share claims + your auth.config.ts
auth.config.ts
JWT DATA
jwt data in paris as copied from jwt.io
Can you grab the jwks from the well known endpoint and add it to jwt.io and see if it passes?
this is the code that I use to generate it, I hope you see the structure
this is the route however when I copy grab it into the jwt.io it doesnt pass. It only passes if I only use the key entry object as in
however just returning this objects gets me the errror jwks is invalid structure
Yeah you'll have to troubleshoot your jwks generation, but sounds like that's the issue.
A valid jwks will work.
could you specify the correct format the jwks should be?
Example generator that uses rs256: https://github.com/get-convex/convex-auth/blob/f279abee8c75a0891d7e0da2d1222b51eefb59c1/src/cli/generateKeys.ts#L11
GitHub
convex-auth/src/cli/generateKeys.ts at f279abee8c75a0891d7e0da2d122...
Library for built-in auth. Contribute to get-convex/convex-auth development by creating an account on GitHub.
thanks @erquhart . will try this and let you know how it goes. Feeling pumped about this. Love the idea of convex
hey @erquhart found out customJwt doesn't work that well with legacy stuff, surprisingly, custom OIDC works really well, and alg has to be RS256. Don't know why, but I'm having a blast with it—shipping to the app store in a few weeks. Thanks for the support.
Nice, glad it’s working! Ed25519 also works (used in the Better Auth component). In case that’s helpful.
Maybe after launch. Ed25519 is significantly faster; however, it's working now, so I'm not touching it 😁
lol absolutely
@Evans since you're doing custom auth stuff, let us know if there are more error messages that would be useful in dev when things aren't configured correctly; we just added more information to auth error messages for dev deployments only
That's the thing; I'm using this package https://pub.dev/packages/convex_flutter, and the developer who built it did a good job. However, the errors are not exposed at all. I've been using your JS solution to try to figure things out on the side. I'm thinking of probably building a new package if I can make the time.
One more thing I'm having trouble with is finding a simple way to interact with your query and mutation api from the Node SDK.
for the sake of buidling fast I'm writing this really really bad code like below. I can't figure out how to make pipelines nor simple population or any advanced querying or mutation so I'm going back and forth the backend redundantly and I'm sure this won't scale.
@erquhart , @Tom do you have any snippets or cheatsheets
PS: Don't judge me for this spaghetti code.
@Tom When building a custom auth and signing your own "JWTs" what "Authenticate" message exactly does to the convex backend? Does it cache the auth for websocket or is it a plain check that validates the token.
@Tom I'm curious because this feels unintuitive when signing from convex for convex. Example: Let's create an HTTP endpoint sign-in that returns refreshToken in cookie and accessToken goes to localstorage. Cool. No problem. But then you plug in the access token into useAuth and fetchAccessToken for auto refresh. And you are immediately refreshing the fresh token.
forceRefreshToken
flag is not what the specification says in this scenario
1. You receive first call with forceRefreshToken = false
provide the token from local storage
2. Pass the authentication
3. You receive second call with forceRefreshToken = true
4. onChange hook is triggered you passed.
I have created an issue today for that https://github.com/get-convex/convex-js/issues/60
I couldn't understand why authenticated token is being refreshed.GitHub
Convex client setAuth wierd behaviour · Issue #60 · get-convex/co...
Question before start: Should convex client call fetchAccessToken with forceRefreshToken: true when this is a newly created token that has just been set? If not there is a wierd behaviour with reac...
The Authenticate message validates the JWT (which might require a network request to get a JWKS) and if it checks out, yes stores for that websocket connection the auth claims like who the user is
The immediate refresh to get a token with the full expiration time, in case the token received was recent enough to be accepted, but doesn't have much time left before it expires
Umm okay I understand your point with close to expire but im on the other side of the spectrum ahhaa
I have all the time 😄
What you're doing looks fine, this JS code runs "in the database," where it's fine to do a loop over e.g. lastMessagesIds. You can do this in parallel for a speedup though, Promise.all etc.
@Tom Would it be possible to have control over this behaviour - disable early refresh?
@machi Sure, want to open an issue? I'm curious about your motivation for this (are auth refreshes expensive for you etc.) and you can already do this by ignoring the forceRefreshToken in your auth handler, but yeah seems reasonable to be able to prevent this
oh you did, nm
Do you know by any chance if refreshes also happen for each of the tabs?
I mean when one tab uses the refresh, browser lock happens other tabs align and swap clientside state
There's no cross-tab coordination in the client (and no requirement that cookies / local storage are being used for the JWT) so yes they should
I think i saw something let me check
GitHub
convex-auth/src/react/client.tsx at f279abee8c75a0891d7e0da2d1222b5...
Library for built-in auth. Contribute to get-convex/convex-auth development by creating an account on GitHub.
Oh if you're talking about Convex Auth, yes
I just meant authentication in the Convex client
Yeah then there is nothing that's true
IMO it's really hard to grasp the auth code in the auth package. There is so much state changing from different sources.
I think that hardest part to track is WHEN useAuth object is being updated and how it influences the underlying setAuth clearAuth.
It would be nice to have more control and nice observer API. So that you can decide what you want to do based on the observed events. And quickly create the state that you need. i.e when I'm loading the initial token I have it instantanously from localStorage.
I'm using it primarily for the entire chat solution for a marketplace. Is there a syntax or specific link to some docs that gives the whole overview of the available api's
Fixing bugs like your clear auth point in the issue is important but beyond that there's not a lot of bandwidth now to add a layer here. Adding a way to subscribe to auth state like https://github.com/get-convex/convex-js/blob/dad3e229d1dd12090ab4361582cde2cb0641cc97/src/browser/sync/client.ts#L784 and implementing existing interfaces in terms of that seems reasonable, and it's mostly React code that manages this so implementing a new set of providers that implements the interface you want is reasonable
no need to use the built-in ConvexAuthenticationProvider, it's a helper to make implementing these less code but there's nothing it does you can't do, the client is the underyling interface
Sure thing throwing out ideas for the record. I really appreciate your time @Tom
@Evans I'm not clear what you're looking for, have you seen the convex docs? https://docs.convex.dev
are there any links to advanced queries? joins and etc.
Some of this is in the docs: https://docs.convex.dev/database/reading-data/#more-complex-queries
General resources on relationships: https://www.convex.dev/can-do/relational-data
Relationships (joins) in Convex overview: https://stack.convex.dev/relationship-structures-let-s-talk-about-schemas
There's a literal api doc, but that's probably not what you want here. Recommend going through the resources above to familiarize.