stack icon indicating copy to clipboard operation
stack copied to clipboard

Integrating with Convex

Open widavies opened this issue 8 months ago • 1 comments

I'm trying to integrate Convex with stack-auth. In the Convex docs, they talk about how to integrate a custom auth provider:

function useAuthFromProviderX() {
  const { isLoading, isAuthenticated, getToken } = useProviderXAuth();
  const fetchAccessToken = useCallback(
    async ({ forceRefreshToken }) => {
      // Here you can do whatever transformation to get the ID Token
      // or null
      // Make sure to fetch a new token when `forceRefreshToken` is true
      return await getToken({ ignoreCache: forceRefreshToken });
    },
    // If `getToken` isn't correctly memoized
    // remove it from this dependency array
    [getToken],
  );
  return useMemo(
    () => ({
      // Whether the auth provider is in a loading state
      isLoading: isLoading,
      // Whether the auth provider has the user signed in
      isAuthenticated: isAuthenticated ?? false,
      // The async function to fetch the ID token
      fetchAccessToken,
    }),
    [isLoading, isAuthenticated, fetchAccessToken],
  );
}

The two parts I'm not clear on how to implement:

  • isLoading - From my understanding, useUser uses a suspense boundary - which is awesome & the right way to do it - but would be nice to have a second version for situations like this that is non-suspense and returns a isLoading flag and maybe even a forceRefreshSession function
  • Function to invalidate/force refresh a session

Let me know if there's a way to do this currently that I'm not seeing.

Thanks so much for this library, this is the future!

@TheCactusBlue @N2D4

widavies avatar Apr 18 '25 02:04 widavies

I believe this should be a correct integration with Convex:

export function useAuthFromStackAuth() {
  const user = useUser();
  const fetchAccessToken = useCallback(
    async ({ forceRefreshToken }: { forceRefreshToken: boolean }) => {
      if (!user) {
        return null;
      }

      if (forceRefreshToken) {
        // Force refresh the access token using the API.
        // This will update the access token in the cookies
        // Some day this can be a built in API?

        const json = await user.getAuthJson();

        const headers = new Headers();

        headers.append('X-Stack-Access-Type', 'client');
        headers.append(
          'X-Stack-Project-Id',
          process.env.NEXT_PUBLIC_STACK_PROJECT_ID || ''
        );
        headers.append(
          'X-Stack-Publishable-Client-Key',
          process.env.NEXT_PUBLIC_STACK_PUBLISHABLE_CLIENT_KEY || ''
        );
        headers.append('X-Stack-Refresh-Token', json.refreshToken || '');
        headers.append('X-Stack-Access-Token', json.accessToken || '');

        const response = await fetch(
          'https://api.stack-auth.com/api/v1/auth/sessions/current/refresh',
          {
            method: 'POST',
            headers,
          }
        );

        const data = await response.json();

        return data['access_token'];
      }

      return await user.getAuthJson().then((res) => res.accessToken);
    },
    // If `getToken` isn't correctly memoized
    // remove it from this dependency array
    [user]
  );
  return useMemo(
    () => ({
      // This is always false because useUser only returns when loaded
      isLoading: false,
      // Whether the auth provider has the user signed in
      isAuthenticated: !!user,
      // The async function to fetch the ID token
      fetchAccessToken,
    }),
    [user, fetchAccessToken]
  );
}

@fomalhautb Does this look correct? If yes, I would like to submit a PR updating your docs to include Convex integration.

This will also require https://github.com/stack-auth/stack-auth/pull/656 to be merged before Convex integration will work

widavies avatar Apr 27 '25 20:04 widavies