Skip to content

Performance is horrible when using recommended Authentication patterns #3997

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
Mikephii opened this issue Apr 15, 2025 · 1 comment
Open

Comments

@Mikephii
Copy link

Which project does this relate to?

Router

Describe the bug

When using the recommended patterns for authentication, particularily in tanstack start, the performance of the app is garbage.

because onBefore load for _root or _authed routes runs on every page navigation, even when you are clientside, it necessitates a sever round trip before being able to navigate resulting in incredibly unresponsive apps. (even if the auth service was processing in 1ms the server trip is usally about 200 -300 ms for most people)

With tanstack router there were some solutions to this by storing the authstate in a react context or hook outside of the inner app and so the state would persist as normal across transitions without a server trip and you could refresh you session tokens as normal when needed.

With tanstack start this is no longer an option as it does not appear as if its possible to have a global state that is persisted across route transitions, (ie client side behaviour that we love, that makes the apps fast).

This is quite obviously not in line with the promise of tanstack start from their very own landing page:

While other frameworks continue to compromise on the client-side application experience we've cultivated as a front-end community over the years, TanStack Start stays true to the client-side first developer experience, while providing a full-featured server-side capable system that won't make you compromise on user experience.

There should be some VERY clear documentation on how to avoid this performance issue, and patterns and practices on how to achieve client first performance.

Your Example Website or App

https://github.com/tanstack/router/tree/main/examples/react/start-clerk-basic

Steps to Reproduce the Bug or Issue

use any of the authenticated template starters and attempt to navigate

Expected behavior

should be an option to have state stored locally and persisted across page navigation, particularily for auth. if that already is an option then there should be docs showing how to use this to avoid the repeated server trips to re-authenticate on every link

Screenshots or Videos

No response

Platform

  • OS: [e.g. macOS, Windows, Linux]
  • Browser: [e.g. Chrome, Safari, Firefox]
  • Version: [e.g. 91.1]

Additional context

No response

@Mikephii
Copy link
Author

Mikephii commented Apr 19, 2025

atleast for supabase currently im determining if someone is authenticated or not by using supabase getSession. im making sure im using the browser client on the browser and the serverClient on the server so there are no round trips required to atleast determine if a user has session cookies. that is in _root

then in _authed, if there is session cookies i make a call to supabase.auth.getUser() through a server fn which will validate the cookie and return user data. this fetch im caching using tanstack queries ensureQueryData queryClient.ensureQueryData(authQueries.user())

then ofcourse every request to supabase is happening server side and using the session cookies and if row level security is set correctly then this is safe.

So, cookies are always fetched locally and allow for instant navigation ✅
getuser is called once within _authed and cached afterwards for instant navigation ✅
security checks happen for all data access serverside and via RLS so no risk of data leaking

It is ofcourse possible for someone to manually set anyKind of session cookies and that will allow them to navigate to a _authed page, but then the first call to supabase.auth.getUser() will throw as they dont have the correct JWT and they will be redirected back to /signin or wherever

the trick is this isomorphic fn to use the supabase/ssr browserClient or serverClient depending on the runtime context.


export type SerializableSession = {
  access_token: string;
  refresh_token: string;
};

export const isoMorphicGetSBSession = createIsomorphicFn()
  .client(async (): Promise<SerializableSession | null> => {
    const { createBrowserClient } = await import('@supabase/ssr');
    const url = import.meta.env.VITE_SUPABASE_URL;
    const anonKey = import.meta.env.VITE_SUPABASE_ANON_KEY;
    const supabase = createBrowserClient(url, anonKey);
    const {
      error,
      data: { session },
    } = await supabase.auth.getSession();
    if (!session) {
      return null;
    }
    return {
      access_token: session.access_token,
      refresh_token: session.refresh_token,
    };
  })
  .server(async (): Promise<SerializableSession | null> => {
    const { parseCookies, setCookie } = await import('vinxi/http');
    const { createServerClient } = await import('@supabase/ssr');
    const supabase = createServerClient(
      process.env.VITE_SUPABASE_URL!,
      process.env.VITE_SUPABASE_ANON_KEY!,
      {
        cookies: {
          getAll() {
            return Object.entries(parseCookies()).map(([name, value]) => ({
              name,
              value,
            }));
          },
          setAll(cookies: any) {
            cookies.forEach((cookie: any) => {
              setCookie(cookie.name, cookie.value);
            });
          },
        },
      }
    );
    const {
      error,
      data: { session },
    } = await supabase.auth.getSession();
    if (!session) {
      return null;
    }

    const {
      error: authErr,
      data: { user },
    } = await supabase.auth.getUser();
    if (authErr) {
      return null;
    }

    return {
      access_token: session.access_token,
      refresh_token: session.refresh_token,
    };
  });

this method is based off of the supabase/ssr auth for sveltkit docs : https://supabase.com/docs/guides/auth/server-side/sveltekit

I assume this method of checking cookies either from the browser or from the server depending on the runtime context, fetching userData once and caching it, and then ensuring that you validate the JWT on serverside actions should work for any auth system and allow for instant navigation.

just need to make sure that whatever api setup youre using you pass and validate your JWT for every data retrieval request and make sure you dont rely on simple userID params for accessing userdata but rather actually determine user access from the decoded JWT.

supabase does this under the hood with getUser() and also with row level security.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant