Antoine
Antoine•18h ago

Custom jwt auth: any working example?

Hi everybody, does anyone have a working demo of the custom JWT auth? I'm trying to make a sample nextjs app working with custom JWT auth, but it doesn't work. Latest behavior: convex auth loops, continuously refetching the JWT. I'm trying to read the source code of the convex client, but it's very time consuming. My repro repo: https://github.com/antoineol/convex-jwt/tree/loop-refetch I created a github issue: https://github.com/get-convex/convex-js/issues/108 , but I don't know where the issue is. My general idea is that I guess there is a special (undocumented) requirement about how useProviderXAuth should behave, but I can't get which one. Any working repo showcasing custom JWT auth, or any idea of what could be wrong would be very appreciated. --- Context: my company has a ruby on rails app (+ devise auth + postgres) for which I'd like to progressively migrate pages to a nextjs + convex app, keeping a good user experience in navigation, preserving the session. It works great (including data sync), except the auth. I ended up doing a minimal repo to find out how JWT auth should work.
2 Replies
Convex Bot
Convex Bot•18h ago
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!
Antoine
AntoineOP•12h ago
My guess is that the issue is around the useAuth hook. Extract from the repro:
<ConvexProviderWithAuth client={convex} useAuth={useJwtAuth}>
{children}
</ ConvexProviderWithAuth>
<ConvexProviderWithAuth client={convex} useAuth={useJwtAuth}>
{children}
</ ConvexProviderWithAuth>
and useJwtAuth.ts:
"use client";

import { useCallback, useEffect, useState } from "react";
import { fetchToken } from "./details/fetchToken";

let tokenPromise: Promise<string> | null = null;

export function useJwtAuth() {
const [isLoading, setIsLoading] = useState(true);
const [isAuthenticated, setIsAuthenticated] = useState(!!tokenPromise);

const fetchAccessToken = useCallback(
async ({ forceRefreshToken = false } = {}) => {
try {
if (forceRefreshToken || !tokenPromise) {
setIsLoading(true);
tokenPromise = fetchToken();
}

const token = await tokenPromise;
setIsAuthenticated(true);
return token;
} finally {
setIsLoading(false);
}
},
[]
);

useEffect(() => {
fetchAccessToken().catch((error) => {
console.error('Error in getToken', error);
});
}, [fetchAccessToken]);

return { isLoading, isAuthenticated, fetchAccessToken: fetchAccessToken };
}
"use client";

import { useCallback, useEffect, useState } from "react";
import { fetchToken } from "./details/fetchToken";

let tokenPromise: Promise<string> | null = null;

export function useJwtAuth() {
const [isLoading, setIsLoading] = useState(true);
const [isAuthenticated, setIsAuthenticated] = useState(!!tokenPromise);

const fetchAccessToken = useCallback(
async ({ forceRefreshToken = false } = {}) => {
try {
if (forceRefreshToken || !tokenPromise) {
setIsLoading(true);
tokenPromise = fetchToken();
}

const token = await tokenPromise;
setIsAuthenticated(true);
return token;
} finally {
setIsLoading(false);
}
},
[]
);

useEffect(() => {
fetchAccessToken().catch((error) => {
console.error('Error in getToken', error);
});
}, [fetchAccessToken]);

return { isLoading, isAuthenticated, fetchAccessToken: fetchAccessToken };
}
Update: it works with a lower-level convex.setAuth(fetchToken): https://github.com/antoineol/convex-jwt/tree/working-custom-jwt-with-setauth (The github issue is updated too) I couldn't find how to make the recommended ConvexProviderWithAuth work. I move on with the setAuth (which makes the job for me), until further news. 🙂

Did you find this page helpful?