v.karbovnichy
v.karbovnichy14mo ago

[Solved] Sending auth info breaks ws connection to Convex

I have next.js app and trying to connect it to NextAuth and Convex. Pretty much successful so far, current step: sending authorized user info to Convex to be used in queries/mutations. When I configure ConvexProviderWithAuth, and it sends JWT to the ws channel (wss://my-tenant.convex.cloud/api/1.10.0/sync), it gets a response: * {"type":"Authenticate","baseVersion":0,"tokenType":"User","value":{"name":"REDACTED","email":"REDACTED@gmail.com","picture":"https://avatars.githubusercontent.com/u/REDACTED?v=4","sub":"kh74hmtwhk1q4py9mwygh0r3fd6nbney","iat":1710630073,"exp":1713222073,"jti":"a7e096a9-0a84-4cd9-9dd6-e42db6cc011e"}} * {"type":"FatalError","error":"Received Invalid JSON on websocket: invalid type: map, expected a string"} As I understand, Convex did not accept the user info. I don't really know what should I change here, maybe send raw JWT directly? Here are the files:
export default function ConvexClientProvider({
children,
}: {
children: ReactNode;
}) {
return (
<ConvexProviderWithAuth client={convex} useAuth={useAuthFromNextAuth}>
{children}
</ConvexProviderWithAuth>
)
}
export default function ConvexClientProvider({
children,
}: {
children: ReactNode;
}) {
return (
<ConvexProviderWithAuth client={convex} useAuth={useAuthFromNextAuth}>
{children}
</ConvexProviderWithAuth>
)
}
export function useAuthFromNextAuth() {
const { data, status } = useSession();

const fetchAccessToken = useCallback(
async ({ forceRefreshToken }: { forceRefreshToken: boolean }) => {
if (forceRefreshToken) {
const response = await fetch("/api/openid/refresh");
return (await response.json()) as string;
} else {
const response = await fetch("/api/openid/token");
return (await response.json()) as string;
}
},
[]
);

return useMemo(
() => ({
isLoading: status === "loading",
isAuthenticated: !!data,
fetchAccessToken,
}),
[fetchAccessToken, data, status]
);
}
export function useAuthFromNextAuth() {
const { data, status } = useSession();

const fetchAccessToken = useCallback(
async ({ forceRefreshToken }: { forceRefreshToken: boolean }) => {
if (forceRefreshToken) {
const response = await fetch("/api/openid/refresh");
return (await response.json()) as string;
} else {
const response = await fetch("/api/openid/token");
return (await response.json()) as string;
}
},
[]
);

return useMemo(
() => ({
isLoading: status === "loading",
isAuthenticated: !!data,
fetchAccessToken,
}),
[fetchAccessToken, data, status]
);
}
3 Replies
v.karbovnichy
v.karbovnichyOP14mo ago
when I wrap in JSON.stringify to make it really a string instead of map return JSON.stringify(await response.json()) as string; then I get another error: {"type":"AuthError","error":"Could not parse as id token","baseVersion":0} Maybe I really need to reencode my own JWT from NextAuth's one (which is encrypted), but what are the options for signing/encrypting? I've added this to the convex dir (however not sure it took it)
export default {
providers: [
{
domain: "https://REDACTED.vercel.app/",
applicationID: "REDACTED",
},
]
};
export default {
providers: [
{
domain: "https://REDACTED.vercel.app/",
applicationID: "REDACTED",
},
]
};
v.karbovnichy
v.karbovnichyOP14mo ago
okay, it seems I was buried in code and focused on getting next-auth working. Re-read the docs on the Convex side this morning, and it's pretty clear that I need to pass raw JWT here. The image on the https://docs.convex.dev/auth/debug pages shows that.
No description
Michal Srb
Michal Srb12mo ago
@v.karbovnichy we now have a guide for setting up Convex with NextAuth: https://stack.convex.dev/nextauth
Convex with Auth.js (NextAuth)
Learn how to use Auth.js with your Next.js server and Convex backend to build a full-featured authentication system.

Did you find this page helpful?