Gorka Cesium
Gorka Cesiumβ€’9mo ago

intermitent failure Clerk: `const identity = await ctx.auth.getUserIdentity();`

Problem: the app works when page loads but if I reload it, the identity is null. In development I have two apps that share the same convex dev in the same monorepo. I run them one a a time in localhost:3000, so when i use one app i don't run the other one. This is the function that is throwing when getUserIdentity. It works fine on the first call, but fails after that.
// convex/page.ts
export const getPage = query({
args: { customerId: v.optional(v.id("bz_customers")) },
handler: async (ctx, { customerId }) => {
const identity = await ctx.auth.getUserIdentity(); // works the first time.
if (!identity) throw new Error("Identidad no encontrada"); // throws on subsequent calls (reload of page or changing the args values from the react component hook useQuery)

//...etc

// convex/page.ts
export const getPage = query({
args: { customerId: v.optional(v.id("bz_customers")) },
handler: async (ctx, { customerId }) => {
const identity = await ctx.auth.getUserIdentity(); // works the first time.
if (!identity) throw new Error("Identidad no encontrada"); // throws on subsequent calls (reload of page or changing the args values from the react component hook useQuery)

//...etc

Dependency: "convex": "^1.11.0", The auth is managed with Clerk. I set a JWT template for each app, with the same applicationID: "convex"
// auth.config.js
// Both env variables are set in dashboard.convex.dev
export default {
providers: [
{ applicationID: "convex", domain: process.env.CLERK_JWT_ISSUER_DOMAIN }, // fentex.com
{
applicationID: "convex",
domain: process.env.BREZZA_CLERK_JWT_ISSUER_DOMAIN, // brezza.com
},
],
};
// auth.config.js
// Both env variables are set in dashboard.convex.dev
export default {
providers: [
{ applicationID: "convex", domain: process.env.CLERK_JWT_ISSUER_DOMAIN }, // fentex.com
{
applicationID: "convex",
domain: process.env.BREZZA_CLERK_JWT_ISSUER_DOMAIN, // brezza.com
},
],
};
The other app works fine. it was the first one that i set up. The failure happens on the second app that I added. The apps are in different url domains. I wonder if it is a dirty state in the auth system. Any idea how can I debug this?
59 Replies
Gorka Cesium
Gorka CesiumOPβ€’9mo ago
"use client";

import { esES } from "@clerk/localizations";
import { ConvexReactClient } from "convex/react";
import { ConvexProviderWithClerk } from "convex/react-clerk";
import { ClerkProvider, useAuth } from "@clerk/nextjs";
const convex = new ConvexReactClient(process.env.NEXT_PUBLIC_CONVEX_URL);

export default function ConvexClientProvider({ children }) {
return (
<ClerkProvider
localization={esES}
publishableKey={process.env.NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY}
>
<ConvexProviderWithClerk client={convex} useAuth={useAuth}>
{children}
</ConvexProviderWithClerk>
</ClerkProvider>
);
}
"use client";

import { esES } from "@clerk/localizations";
import { ConvexReactClient } from "convex/react";
import { ConvexProviderWithClerk } from "convex/react-clerk";
import { ClerkProvider, useAuth } from "@clerk/nextjs";
const convex = new ConvexReactClient(process.env.NEXT_PUBLIC_CONVEX_URL);

export default function ConvexClientProvider({ children }) {
return (
<ClerkProvider
localization={esES}
publishableKey={process.env.NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY}
>
<ConvexProviderWithClerk client={convex} useAuth={useAuth}>
{children}
</ConvexProviderWithClerk>
</ClerkProvider>
);
}
using Next.js app router layout.js
export default function RootLayout({ children }) {
return (
<>
{process.env.NEXT_PUBLIC_NODE_ENV === "production" ? (
<LogRocketComponent />
) : null}

<ConvexClientProvider>
<html lang="es-MX" className="antialiased">
<body
className={`bg-stone-50 dark:bg-stone-900 dark:text-stone-200 print:bg-white ${inter.className} `}
>
{children}
<Toaster richColors />
</body>
</html>
</ConvexClientProvider>
</>
);
}
export default function RootLayout({ children }) {
return (
<>
{process.env.NEXT_PUBLIC_NODE_ENV === "production" ? (
<LogRocketComponent />
) : null}

<ConvexClientProvider>
<html lang="es-MX" className="antialiased">
<body
className={`bg-stone-50 dark:bg-stone-900 dark:text-stone-200 print:bg-white ${inter.className} `}
>
{children}
<Toaster richColors />
</body>
</html>
</ConvexClientProvider>
</>
);
}
Nishil Faldu
Nishil Falduβ€’9mo ago
I have also been facing this issue and have been using a workaround (probably not the best one) and would like someone to comment on this if they have a nice solution πŸ˜ƒ
FleetAdmiralJakob πŸ—• πŸ—— πŸ—™
I think I encounter the same error Somehow if I delete all cookies and the localstorage and try to signin again everything works again for 1 time this is especially painful if you do something like this: signup -> singout -> singup with new account I actually think this is the root problem of this: https://discord.com/channels/1019350475847499849/1224835051511091391
Gorka Cesium
Gorka CesiumOPβ€’9mo ago
sounds related I wonder if the applicationID has to be unique in auth.config.js
FleetAdmiralJakob πŸ—• πŸ—— πŸ—™
What do you mean excactly?
Gorka Cesium
Gorka CesiumOPβ€’9mo ago
I have this
export default {
providers: [
{ applicationID: "convex", domain: process.env.CLERK_JWT_ISSUER_DOMAIN }, // fentex.com
{
applicationID: "convex",
domain: process.env.BREZZA_CLERK_JWT_ISSUER_DOMAIN, // brezza.com
},
],
};
export default {
providers: [
{ applicationID: "convex", domain: process.env.CLERK_JWT_ISSUER_DOMAIN }, // fentex.com
{
applicationID: "convex",
domain: process.env.BREZZA_CLERK_JWT_ISSUER_DOMAIN, // brezza.com
},
],
};
so probably in the second applicationID it should be different so they are unique but if i change the value it won't work at all
FleetAdmiralJakob πŸ—• πŸ—— πŸ—™
You also have to change the name in the Clerk Dashboard Can you confirm that the minimal reproduction I made has this error you describe? Just to make sure that I understand everything correctly
Gorka Cesium
Gorka CesiumOPβ€’9mo ago
I can change the domain by changing the env variables in the dashboard. But if I change the applicationID in auth.config.js and in the clerk dashboard JWT template, then i get errors about it and login won't work
FleetAdmiralJakob πŸ—• πŸ—— πŸ—™
Interesting. I meant the JWT Template and applicationID so interesting that it does not work
Gorka Cesium
Gorka CesiumOPβ€’9mo ago
I'm using the Clerk SignIn components. Not sure how to reproduce. I'm not creating users from the app
FleetAdmiralJakob πŸ—• πŸ—— πŸ—™
Ok, shoud have nothing todo with the way we log into Clerk anyways
Gorka Cesium
Gorka CesiumOPβ€’9mo ago
maybe it is a Clerk error another different thing in the app that is failing is that instead of a users table i'm using bz_users but i don't think that has anything to do with identity
FleetAdmiralJakob πŸ—• πŸ—— πŸ—™
Sure, in but in my reproduction app that is not the case so this is not the problem here. Possible, but I don't think so
Gorka Cesium
Gorka CesiumOPβ€’9mo ago
@FleetAdmiralJakob πŸ—• πŸ—— πŸ—™ are you using app router with RSC pages?
Michal Srb
Michal Srbβ€’9mo ago
I’m not sure what could be causing this. To change the applicationId, change the aud field in the Clerk JWT token template. That’s where the value β€œcoomes from”. The other things i would try: 1. Maybe the Clerk config has an issue. Create a new one. 2. Run the apps on different ports.
FleetAdmiralJakob πŸ—• πŸ—— πŸ—™
Yes But is this limited to app router?
Gorka Cesium
Gorka CesiumOPβ€’9mo ago
not sure, i'll try making all pages "use client" seems like if the auth cookie is lost
TwendyKirn
TwendyKirnβ€’9mo ago
Hello, is this issue supposed to be pinned in Support channel instead of Convex Search? @Michal Srb
FleetAdmiralJakob πŸ—• πŸ—— πŸ—™
do you have u block origin or any other adblocker. I never tried it out without an adblocker so maybe that's the problem.
Gorka Cesium
Gorka CesiumOPβ€’9mo ago
i used in Chrome and Safari. The thing is that it works fine with the other app that shares de DB. And i copied the setup, so it is very strange. I even tried running app2 in localhost:3001 instead of localhost:3000 does it happens to you with one app using the db or multiple apps sharing db?
Gorka Cesium
Gorka CesiumOPβ€’9mo ago
crazy. do you have other apps that work fine with clerk+convex? i have a bunch working well.. it is just this one that is failing
FleetAdmiralJakob πŸ—• πŸ—— πŸ—™
no, i have a real app having the same problem
Gorka Cesium
Gorka CesiumOPβ€’9mo ago
which is your workaround? you don't validate identity on requests? i also use pnpm
FleetAdmiralJakob πŸ—• πŸ—— πŸ—™
okay, maybe we have to try with npm and yarn also does it work with the pages router?
Gorka Cesium
Gorka CesiumOPβ€’9mo ago
but my other projects work fine with pnpm
FleetAdmiralJakob πŸ—• πŸ—— πŸ—™
I agree weird. But the other projects work fine in general
Gorka Cesium
Gorka CesiumOPβ€’9mo ago
i only use app router on these ones..
FleetAdmiralJakob πŸ—• πŸ—— πŸ—™
okay, but maybe it helps us identify the problem I can also write a new minimal repro for the pages router
Gorka Cesium
Gorka CesiumOPβ€’9mo ago
i'm rolling back to clerk 4.29.9 that didnt help
Gorka Cesium
Gorka CesiumOPβ€’9mo ago
I noticed in the network tab that when it loads a route that works it shows _rsc in the payload.
Good
Bad
Michal Srb
Michal Srbβ€’9mo ago
Can you share the useQuery callsite? Do you have the component inside Authenticated? Have you checked the auth debugging guide?
FleetAdmiralJakob πŸ—• πŸ—— πŸ—™
I have checked the auth debugging site. Her is what I found: The JWT Token that is passed from the client is the correct one and works. Despite that on the server side ctx.auth.getUserIdentity (or something like that) returns null The debugging site says that something is misconfigure but it works for one time and and after that not anymore so I think it has to be a bug in Convex at this point Michal. Can you validate that my reproduction has this error and that my reproduction is correctly configured?
Gorka Cesium
Gorka CesiumOPβ€’9mo ago
I have the same behavior. I also noticed that in mutations it works well yes i checked the auth debugging guide and saw the token logging once, and after that it logs null
FleetAdmiralJakob πŸ—• πŸ—— πŸ—™
I use mutations in my reproduction and app (and have the error) so it is not limited to queries and actions
Gorka Cesium
Gorka CesiumOPβ€’9mo ago
I also noticed that the other app that i thought it was fine it also fails with identity on queries ok, I just realized that on my other apps i was only using const identity = await auth.getUserIdentity(); on mutations... so i had a false sense of success.
FleetAdmiralJakob πŸ—• πŸ—— πŸ—™
The other question is: this seems to be a general issue with convex + clerk, but why aren't there much others having this issue? can you show us your workaround?
sshader
sshaderβ€’9mo ago
To echo what Michal said earlier -- a really common cause of getUserIdentity being null is calling the function without gating on the Convex isAuthenticated or rendering within the Authenticated component. It's not enough to check using the clerk hooks (between steps 4 and 7 in https://docs.convex.dev/auth/clerk#under-the-hood is where clerk auth is set but hasn't been propagated to convex yet) Looking at the callsites (the useQuery and useMutation calls) in your client code will probably be the most helpful in terms of debugging (and where we're checking Concretely, I took a look at https://github.com/FleetAdmiralJakob/minimal-reproduction-convex-clerk/blob/bc2eab8e0f117babe299bae3cf7210f6661bdb3d/src/app/page.tsx#L33C10-L33C23 and it looks like we're calling the mutation without checking that isAuthenticated is true? Instead of calling the mutation immediately, could you store state that a sign up was created and have a separate useEffect that calls the mutation if there was a sign up and isAuthenticated is set?
GitHub
minimal-reproduction-convex-clerk/src/app/page.tsx at bc2eab8e0f117...
Contribute to FleetAdmiralJakob/minimal-reproduction-convex-clerk development by creating an account on GitHub.
Convex Clerk | Convex Developer Hub
Clerk is an authentication platform providing login via
Gorka Cesium
Gorka CesiumOPβ€’9mo ago
should we wrap with <Authenticated> every component using useQuery and useMutation?
sshader
sshaderβ€’9mo ago
Every component where the query / mutation expects getUserIdentity to be populated. Other options are to conditionally call your functions (e.g. useQuery(api.my.query, isAuthenticated ? args : "skip")) or handling getUserIdentity being null in the functions (returning a placeholder value until auth has propagated through). I'm not an expert on auth with Convex, and the approach you take depends on the kind of app you're building. For the personal projects I've built, I've usually just wrapped the root of my app (or a few key pages) with Authenticated and had all my functions assume getUserIdentity would be set
Gorka Cesium
Gorka CesiumOPβ€’9mo ago
ooh, I didn't know that but makes a lot of sense. I didn't see that mentioned in the convexclerk guides.
sshader
sshaderβ€’9mo ago
Yeah I agree we can better document this (I believe we already have some stuff in the works here)
Gorka Cesium
Gorka CesiumOPβ€’9mo ago
yeah it's an important point for safety. i do like better the approach from clerk that assumes everything must be authenticated and you have to manually declare public routes. This is a recommended best practice in app security. So it would be great at the auth.config.js to toggle if you want full auth expectation and whitelist individually the functions that dont need auth
ian
ianβ€’9mo ago
btw, if you change the template name from "convex" you can't currently use ConvexProviderWithClerk since it hard-codes the "convex" template in useUseAuthFromClerk @Michal Srb @Gorka Cesium is this all set now? Was the main difference the fact that it was Next.js and a combo of authentication passes w/ intermediate state where convex wasn't authenticated yet, maybe b/c of SSR, solved by wrapping more things in <Authenticated>?
FleetAdmiralJakob πŸ—• πŸ—— πŸ—™
interesting, I will try out using useEffect and isAuthenticated
Gorka Cesium
Gorka CesiumOPβ€’9mo ago
I just tried it but still gets identity null finally, I found out that isAuthenticated does work βœ… , but <Authenticated> doesnt help with the issue. So i am only going to use isAuthenticated like @sshader suggested
ian
ianβ€’9mo ago
I wonder if the <Authenticated> is in a server component & if that's related. Sorry it's been so hard to debug. What changes to https://docs.convex.dev/auth/debug would be helpful?
Debugging Authentication | Convex Developer Hub
You have followed one of our authentication guides but something is not working.
Gorka Cesium
Gorka CesiumOPβ€’9mo ago
I was trying to use Authenticated in a component with 'use client';. It does hide things if the user is unauth, but it doesn't help with the ctx.auth.getUserIdentity() so seems that isAuthenticated is more in sync with convex than Authenticated in my opinion the user shouldn't have to skip useQuery depending on isAuthenticated. In that case i might as well use the useUser hook and pass the user data as an arg to query
ian
ianβ€’9mo ago
yeah we need to make this cleaner. One thing about using useUser is that you can't necessarily trust user data sent as arguments. A client could send up user data from a different user.
Gorka Cesium
Gorka CesiumOPβ€’9mo ago
yeah thats the thing
Michal Srb
Michal Srbβ€’9mo ago
@Gorka Cesium Authenticated just calls useConvexAuth under the hood, so it would be very surprising if it behaved differently. If you can get a repro of that issue that would be valuable.
Nishil Faldu
Nishil Falduβ€’9mo ago
There were some really good points made in this conversation and helped me decide the next steps. Thank you all!
Gorka Cesium
Gorka CesiumOPβ€’9mo ago
interesting that in my setup i don't need to use isAuthenticated or Authenticated for mutations.
Dan.Griff
Dan.Griffβ€’9mo ago
I have the same problem…! will be resolving today based on the comments so far! Thank you all πŸ™
Michal Srb
Michal Srbβ€’9mo ago
interesting that in my setup i don't need to use isAuthenticated or Authenticated for mutations.
Mutations usually happen based on some user action, and by that time the auth has been setup. The Authenticated component really just makes the client "wait" before subscribing to queries in its children (by not rendering its children).
Nishil Faldu
Nishil Falduβ€’9mo ago
Components with queries and mutations wrapped with <Authenticated/> and a custom skeleton wrapped in <AuthLoading/> has been working great for me so far!
Michal Srb
Michal Srbβ€’9mo ago
@Gorka Cesium @Nishil Faldu @Dan.Griff I'm interested whether you read through the Clerk docs first, or looked at the "example" repo linked from the top of the docs, or some other template? Trying to figure out how to guide folks better to the correct setup.
Gorka Cesium
Gorka CesiumOPβ€’9mo ago
i went through the clerk guide and then the docs and then example repo. I never saw that it was a requirement to use isAuthenticated. I thought that ClerkProvider would take care of it. I look forward to the Convex Auth solution
Nishil Faldu
Nishil Falduβ€’9mo ago
for me https://docs.convex.dev/auth/clerk#under-the-hood this link on convex docs was very helpful for my understanding (of how things work) and I didn't actually know that <Authenticated/> and other Auth components exist lol - so it was great knowing about those! After that it made sense that the auth takes some time to load and I must use some loading indicator before I render the component with queries - hence the solution
Convex Clerk | Convex Developer Hub
Clerk is an authentication platform providing login via

Did you find this page helpful?