auth-helpers icon indicating copy to clipboard operation
auth-helpers copied to clipboard

user from useUser are sometimes null when coming back from redirect after third party sign in

Open Coci0AlexMeew opened this issue 3 years ago • 5 comments

Hi Supabase community.

I have found that sometimes when successfully signing in with third party providers (Facebook, Twitter and Google) the user returned from useUser hook is null even though the user on server side is signed in (checked logs + fragment with tokens is returned correctly). It happens only sometimes when signing in the first time in a new browser / tab session.

It happens like this.

  1. Build app
  2. Call signIn with facebook provider (or other provider)
  3. Redirects to provider link
  4. Successful sign in at provider
  5. Redirects back to app (this is where useUser returns null user)

As the fragment with tokens are returned correctly a temporary fix is getting it and signing the user in manually like so: App (pages/index.tsx)

export default function App() {
  const { user, isLoading } = useUser();

  const router = useRouter();
  const isSignedIn = user;
  let isWaitingForSignIn = false;
  let refreshToken: string;

  if (!isSignedIn) {
    /**
     * Get fragment params. Will only exist if bug happens in supabase helpers -
     * causing the successful signin with third party to not be registered -
     * here on client side after re-direct from provider.
     */
    const fragmentParams = getFragmentParams(router.asPath);
    refreshToken = fragmentParams.refresh_token;

    if (refreshToken) {
      /*
      Function to manually sign user in with refresh token from fragment.
    */
      const signUserInWithRefreshToken = () =>
        authService.signIn({
          refreshToken,
        });
      /*
       * To avoid sign in page flicker while waiting for sign in.
       * Page will re-render after successful sign in and isWaitingForSignIn -
       * will be set to false while isSignedIn will be true
       */
      isWaitingForSignIn = true;
      signUserInWithRefreshToken();
    }
  }

  if (isLoading || isWaitingForSignIn) {
    // eslint-disable-next-line @typescript-eslint/ban-ts-comment
    // @ts-ignore
    return <AuthLoadingScreen />;
  }
  /* is signed in */
  if (isSignedIn) return <SignedInPage />;

  return <SigninPage />;
}

getFragmentParams.ts

export const getFragmentParams = (asPath: string) => {
  const urlSearchParams = new URLSearchParams(asPath.split('#')[1]);
  const fragmentParams = Object.fromEntries(urlSearchParams.entries());
  return fragmentParams;
};

Project info:

  • Expo sdk 44
  • Next js 11.0.1
  • Supabase js 1.30.7
  • Supabase auth helpers 1.2.3

Coci0AlexMeew avatar Mar 25 '22 18:03 Coci0AlexMeew

Same here :(

gperdomor avatar Apr 03 '22 00:04 gperdomor

The issue seems to be this one: https://github.com/supabase/supabase/discussions/6080

psteinroe avatar Apr 04 '22 14:04 psteinroe

Can you upgrade to the latest version and see if that resolves it?

thorwebdev avatar May 10 '22 10:05 thorwebdev

I'm experiencing this issue with the latest version. I have a hook like such:

export const RequireAuth = (callback = () => {}) => {
  const { user, isLoading } = useUser();
  const router = useRouter();

  useEffect(() => {
    if (!isLoading) {
      if (user) {
        callback();
      } else {
        router.replace("/login");
      }
    }
  }, [isLoading, user, router]);
};

When I include this hook in a page that is redirected to after signup via magic link, it redirects the user despite being logged in.

reedrosenbluth avatar Jun 01 '22 18:06 reedrosenbluth

I'm experiencing a similar issue as well using magic link sign in. The magic link we send users points to a page protected by the withPageAuth helper - the helper redirects the user before the user is logged in.

jimmythigpen avatar Jun 24 '22 18:06 jimmythigpen

As it stands today, you will need to set the redirect URL to be a client-side route, e.g. your sign-in page, and then have a hook that monitors the user object and redirects to the desired page, e.g. see this example: https://github.com/vercel/nextjs-subscription-payments/blob/main/pages/signin.tsx#L58-L62

const { user } = useUser();

useEffect(() => {
    if (user) {
      router.replace('/account');
    }
  }, [user]);

That's because the OAuth sign-in state is currently communicated via a URL fragment and therefore can't be accessed server-side.

thorwebdev avatar Sep 28 '22 06:09 thorwebdev