Skip to content

Commit afd8097

Browse files
authored
improvements (#8)
## Problem The Convex integration wasn't properly handling token refresh requests. When Convex called `fetchAccessToken({ forceRefreshToken: true })`, the implementation ignored this parameter and returned a stale cached token, causing authentication failures after token expiry. ## Solution - Implemented proper handling of the `forceRefreshToken` parameter - When `forceRefreshToken: true`, calls WorkOS's `refresh()` to get a new token - When `forceRefreshToken: false`, uses `getAccessToken()` which auto-refreshes if needed - Removed problematic token caching that was preventing refresh - Fixed loading state calculation to prevent blank pages during token refresh
1 parent 626846e commit afd8097

File tree

1 file changed

+26
-20
lines changed

1 file changed

+26
-20
lines changed

components/ConvexClientProvider.tsx

Lines changed: 26 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
'use client';
22

3-
import { ReactNode, useCallback, useRef, useState } from 'react';
3+
import { ReactNode, useCallback, useState } from 'react';
44
import { ConvexReactClient } from 'convex/react';
55
import { ConvexProviderWithAuth } from 'convex/react';
66
import { AuthKitProvider, useAuth, useAccessToken } from '@workos-inc/authkit-nextjs/components';
@@ -19,30 +19,36 @@ export function ConvexClientProvider({ children }: { children: ReactNode }) {
1919
}
2020

2121
function useAuthFromAuthKit() {
22-
const { user, loading: isLoading } = useAuth();
23-
const { accessToken, loading: tokenLoading, error: tokenError } = useAccessToken();
22+
const { user, loading } = useAuth();
23+
const { accessToken, getAccessToken, refresh } = useAccessToken();
24+
2425
const hasIncompleteAuth = (!!user && !accessToken) || (!user && !!accessToken);
25-
const loading = (isLoading ?? false) || (tokenLoading ?? false) || hasIncompleteAuth;
26+
const isLoading = loading || hasIncompleteAuth;
2627
const authenticated = !!user && !!accessToken;
2728

28-
// Memoize the token to prevent unnecessary changes
29-
const stableAccessToken = useRef<string | null>(null);
30-
if (accessToken && !tokenError) {
31-
stableAccessToken.current = accessToken;
32-
}
33-
34-
const fetchAccessToken = useCallback(async () => {
35-
// WorkOS AuthKit automatically refreshes tokens before they expire,
36-
// so we don't need to manually handle forceRefreshToken to avoid infinite loops.
37-
// This is the recommended approach for WorkOS + Convex integration.
38-
if (stableAccessToken.current && !tokenError) {
39-
return stableAccessToken.current;
40-
}
41-
return null;
42-
}, [tokenError]);
29+
// Create a stable fetchAccessToken function
30+
const fetchAccessToken = useCallback(
31+
async ({ forceRefreshToken }: { forceRefreshToken?: boolean } = {}): Promise<string | null> => {
32+
if (!user) {
33+
return null;
34+
}
35+
36+
try {
37+
if (forceRefreshToken) {
38+
return (await refresh()) ?? null;
39+
}
40+
41+
return (await getAccessToken()) ?? null;
42+
} catch (error) {
43+
console.error('Failed to get access token:', error);
44+
return null;
45+
}
46+
},
47+
[user, refresh, getAccessToken],
48+
);
4349

4450
return {
45-
isLoading: loading,
51+
isLoading,
4652
isAuthenticated: authenticated,
4753
fetchAccessToken,
4854
};

0 commit comments

Comments
 (0)