supabase-js: Local Storage getting cleared randomly, causing user to log out

Bug report

Describe the bug

Sometimes, the local storage entry for Supabase Auth (supabase.auth.token) gets cleared, causing the user to be logged out of the application. Local storage keys unrelated to Supabase are unchanged and remain persisted, leading me to believe that this is a Supabase issue.

To Reproduce

I’m not entirely sure how to reproduce this. It happens consistently for me, but I don’t know the cause. Usually, after logging in and waiting for a few days, the local storage entry gets cleared automatically.

You can try the following:

  1. Implement client-side Supabase Auth
  2. Log into the application
  3. Wait some period of time (usually 1-2 days)
  4. Observe that you are logged out and the local storage entry has been cleared

For details on how I am implementing Supabase Auth, take a look at my public repository.

Expected behavior

supabase.auth.token should never be cleared unless the user explicitly logs out or clears their cache.

System information

This happens on multiple OS’s and browsers.

  • OS: Windows, Android, iOS
  • Browser (if applies): Chrome, Safari
  • Version of supabase-js: 1.22.5

Additional context

My JWT expiry is 604800, but I’ve also used 60 before with the same behavior happening. It seems to be unaffected by the JWT expiry value.

This happens both on localhost and in production.

I’m not sure about the implementation details of Supabase Auth, but what could be happening is that the refreshToken that I am passing into the signIn method is expired, so the user gets automatically logged out and the local storage entry is cleared. What should happen is that the token is automatically refreshed, the user stays signed in, and the local storage entry is preserved/updated.

Edit: I’ve noticed this happening sometimes after I deploy. Is there something that changes after each deployment that invalidates the session? I’m using Vercel.

About this issue

  • Original URL
  • State: closed
  • Created 3 years ago
  • Reactions: 10
  • Comments: 21 (4 by maintainers)

Most upvoted comments

Why it takes too long to solve this issue 😦 ?

Still a problem in Nuxt/Supabase as well

This is still a problem with React Native as of writing (October 11th 2022)

No messages are logged in the console.

It’s fine and good that the access token expires, but a new access token should be retrieved when this happens. The user should not be logged out. If you look at popular websites like Google, YouTube, even GitHub, you’re pretty much always logged in, and I expect the same to happen for my website. It’s a big problem for me because many of my users are on mobile devices where it’s a hassle to keep logging in every day.

For what’s it worth, Firebase Auth does not have this problem. You are always logged in unless you explicitly log out.

It’s also fine if Supabase Auth chooses not to make this the default behavior (although I definitely think it should be), but there should be some way to keep the user signed in.

I don’t know if it will help you because it was pretty specific to my implementation, but here is how I solved it. I am listening to the person table which is a table that I created and linked to the users table generated by Supabase (it’s just holding more details about the user.) I created a layer of abstraction to listen for changes and return an Observable that returns Either a Failure or an instance of the retrieved/updated Person. The error I had was when I tried to unsubscribe from the stream when refreshing the page. I had a warning saying that the stream was already closed or something. All I had to do was to remove the call from the return method of the useEffect hook for it to work.

  useEffect(() => {
    let handleSessionSubscription: Subscription | null;
    const maybeSession = supabase.auth.session();
    handleSessionSubscription = handleSession(maybeSession);
    const { data: subscription } = supabase.auth.onAuthStateChange(
      (_event, session) => {
        handleSessionSubscription = handleSession(session);
      }
    );
    return () => {
      // Here be dragons 🐉
      // 👇 This was giving me an error, I removed it
      handleSessionSubscription?.unsubscribe();
      subscription?.unsubscribe();
    };
  }, []);

My handleSession method :

  const handleSession = (session: Session | null) => {
    if (null === session || null === session.user) {
      setAuthStateUnauthenticated();
      return null;
    }
    const user = session.user;
    const id = new UniqueId(user.id, 'fromUniqueString');
    return personRepository.watchPerson(id).subscribe((failureOrPerson) => {
      failureOrPerson.fold(toastFailure, (personSuccess) => {
        const person = personSuccess.data;
        setAuthStateAuthenticated({ person: person });
      });
    });
  };

Check for any log messages in your browser’s console. Waiting a few days and expecting the session to still be persisted is a vulnerability.

Eventually, the access token will expire. If the refresh token cannot obtain a new access token then the session is automatically deleted.