#useConvexAuth() returns isAuthenticated: false even though token exists in localStorage after login

7 messages · Page 1 of 1 (latest)

lapis yew
#

I'm using @convex-dev/auth (v0.0.90) with convex (v1.31.0) and React 19. After completing OAuth (Google) or password sign-in:

What I observe:

  • useAuthToken() returns the JWT token (it's stored in localStorage as _convexAuthJWT...)
  • useConvexAuth() returns { isAuthenticated: false, isLoading: false }
  • A manual hard refresh (Cmd+Shift+R) fixes it - then isAuthenticated: true

My setup:
// main.tsx
const convex = new ConvexReactClient(import.meta.env.VITE_CONVEX_URL)
<ConvexAuthProvider client={convex}>
<App />
</ConvexAuthProvider>

What I've tried:

  • Clearing localStorage/sessionStorage/cookies
  • window.location.reload() doesn't fix it, only hard refresh does
  • Backend logs show auth:signIn and auth:store succeed
  • SITE_URL env var is set correctly in Convex dashboard

Question: Is there a known issue where the WebSocket doesn't pick up the token from localStorage after sign-in? Is there a way to force re-authentication without a hard refresh?

crystal dawnBOT
#

Hi @lapis yew. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave:

#

This pattern usually means:

  1. The Convex client is not (yet) sending a JWT over the WebSocket, even though the token exists in localStorage.
  2. Or the Convex client’s auth state got into a bad state and only a full reload (clearing some in‑memory state) recovers it.

From the docs and support threads, here are the concrete things to check and ways to debug/mitigate.


1. Confirm the WebSocket is (not) sending the token

Follow the “Step 2” debugging guide to see if Convex is actually authenticating over the WS:

  • Open DevTools → Network.
  • Filter by WS and select the sync WebSocket.
  • In Messages, look for a message with type: "Authenticate" and a long value (your JWT). [Debug frontend]

If you don’t see an Authenticate message with a token right after sign‑in, the issue is on the frontend: the Convex client is not picking up the new token.

If you do see the token, but your backend still sees no identity, then the bug is on the backend side (JWT validation / config) — but note that if useConvexAuth().isAuthenticated is false, support explicitly says “jwt validation is not working yet”. [getAuthUserId thread]


2. Verify that Convex is wired to your auth correctly

For Convex to know about your auth state you must use the auth-aware provider:

  • With @convex-dev/auth/react you should be using its ConvexAuthProvider (which is a ConvexProviderWithAuth wrapper) and your Convex deployment must be configured with the corresponding provider in auth.config.ts/auth.config.mts. [React API]
#

When useConvexAuth() returns { isAuthenticated: false, isLoading: false } but you already have a token, that typically means one of:

  • The provider configuration on the backend doesn’t match the JWT (wrong domain/client id/audience, etc.), so Convex rejects the token and falls back to unauthenticated. There are multiple provider‑specific “debugging authentication” sections that all say: if login succeeds but useConvexAuth is false, check auth.config.ts/convex/auth.config.ts and re‑deploy after changes. [Auth0 debug; Clerk debug]
  • The Convex client is simply not sending any token at all (Step 2 above).

Given that a hard refresh fixes it, the second is more likely: your client layout after sign‑in is raced or mis‑initialized.


3. Race conditions right after sign‑in

Convex auth is a two‑step process:

  1. Your auth library gets/stores the JWT (what useAuthToken() is reading).
  2. Convex’s WebSocket client detects the new token and sends it to the backend; only after this does useConvexAuth() flip to isAuthenticated: true. [Redirect race thread]

Support explicitly notes that you must avoid calling authenticated queries or redirect logic before useConvexAuth().isAuthenticated becomes true; otherwise you’ll see a brief unauthenticated state or failures that then fix themselves. [Redirect race thread]

In your case, if you:

  • Complete sign‑in,
  • Immediately render <ConvexAuthProvider client={convex}> and also remount the client / auth tree, or immediately call authed queries,
#

you can end up in a state where Convex hasn’t yet re‑authenticated the WebSocket, and the auth manager ends up stuck until a full hard reload resets everything.

Mitigation pattern (from support):

  • Delay navigation or any protected logic until useConvexAuth() itself says isAuthenticated: true:
const { isAuthenticated, isLoading } = useConvexAuth();

useEffect(() => {
  if (!isLoading && isAuthenticated) {
    // now safe to navigate / show protected UI
  }
}, [isLoading, isAuthenticated]);

[Redirect race thread]

This doesn’t “force” Convex to re‑auth, it just prevents you from depending on authenticated state before Convex has actually sent the token.


4. Debug the client side more deeply

Since this feels like a client‑side auth manager issue, enable verbose logging:

const convex = new ConvexReactClient(import.meta.env.VITE_CONVEX_URL, {
  verbose: true,
});

[Auth debugging logs]

Then:

  • Watch for logs about “authenticate” and token changes during sign‑in.
  • Confirm whether Convex logs that it is trying to authenticate after you get the token, or only on full reload.

If logs don’t show any re‑authentication after sign‑in (only on page load), that strongly suggests a bug / limitation in the current client, consistent with other reports of Convex auth state sometimes reflecting unauthenticated until a refresh, especially after backgrounding / longer inactivity. [Frequent logouts thread]


5. Forcing re‑authentication (workarounds)

The knowledge sources don’t document an official “force re‑auth” API on the client. They do say:

#
  • ConvexProviderWithAuth will re‑call fetchAccessToken() whenever its useAuth hook changes identity, and will move auth state back to loading and then call fetchAccessToken again. [React API]

For your setup with ConvexAuthProvider from @convex-dev/auth/react, that provider owns the useAuth implementation. There’s no documented way in the sources above to manually “poke” it to refresh, beyond causing a full re‑mount / identity change.

So, based on what’s documented:

  • There is no officially documented client‑side “force reauth” method you can call, other than full reload.
  • The intended path is that the auth integration’s fetchAccessToken gets called automatically whenever identity changes; if that’s not happening until a hard reload, it’s likely a bug in the current @convex-dev/auth + client combo, not something you can fully fix from userland.

6. Summary / what you can do now

From the docs and threads:

#
  1. Verify the WS auth message as in Step 2 to be sure the token is (not) being sent. [Debug frontend]
  2. Enable verbose: true on ConvexReactClient to see auth logs. [Auth debugging logs]
  3. Guard your app logic on useConvexAuth(), not on “token exists in localStorage”:
    • Don’t run authed queries or redirects until isAuthenticated: true and isLoading: false. [Redirect race thread]
  4. If, after sign‑in, you never see an Authenticate WS message nor client logs about re‑auth until a hard reload, that appears to be a client bug. The sources don’t describe a workaround beyond full reload or waiting for improved client auth state handling. [Frequent logouts thread]

The knowledge sources don’t document a clean, supported API to “force re-authentication without a hard refresh”, so beyond the patterns above, I can’t point you to a specific function to call.

Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: