vercel / nextjs-subscription-payments

Clone, deploy, and fully customize a SaaS subscription application with Next.js.
https://subscription-payments.vercel.app/
MIT License
6.27k stars 1.28k forks source link

SignIn from email link, useUser() return leads to redirect bug #116

Closed tatwater closed 1 year ago

tatwater commented 2 years ago

When I click the signin/signup link in email and get redirected to the app, I miss the page I made to welcome new users and gather more info for their profile because there's a moment where useUser() says it's no longer loading, but it doesn't provide a user object to my page.

In this demo all users will land on the welcome page regardless of whether or not they are new. I added http://localhost:3000/welcome as an Additional Redirect URL in Supabase and I pass { redirectTo: 'http://localhost:3000/welcome' } to supabaseClient.auth.signIn() which seems to have worked out, even for magic emails.

I've created a barebones pages/welcome.tsx file just to see what the values / steps are for useUser():

function WelcomePage() {
  const { isLoading, user, userDetails } = useUser();

  supabaseClient.auth.onAuthStateChange((event, session) => {
    console.log(event, session);
  });

  console.log(isLoading, user, userDetails);

  useEffect(() => {
    if (!isLoading && !user)
      router.replace('/signin');
  }, [isLoading, user]);

  if (isLoading) {
    return (
      <span>Loading...</span>
    );
  }

  if (user) {
    return (
      <span>{ user.email }</span>
    );
  }

  return (
    <span>Redirecting...</span>
  );
}

And here's what prints to the console:

true null undefined
[Fast Refresh] rebuilding
false null undefined
SIGNED_IN {...object with access_token, user, etc...}
SIGNED_IN {...object with access_token, user, etc...}
true null undefined
// The SIGNED_IN/object about four more times
true null undefined
true {...object with id, aud, email, etc...} undefined
false {...object with id, aud, email, etc...} undefined

So as you can imagine, that one instant where it stops loading but user is still null pushes me back to /signin. But onAuthStateChange() knew there was a valid user even before useUser() returned null two more times!

My pages/signin.tsx has a similar check for user, where if it finds one it calls router.replace('/') to send the user back to the homepage. This whole process flickers by in an instant and a new user would miss my welcome page entirely.

It appears as though that false null print happens exactly when #access_token=... is removed from the URL (I screen recorded so I could move frame-by-frame through the redirects).

Returning to /welcome and refreshing prints the following to the console:

true null undefined
[Fast Refresh] rebuilding
true null undefined
true {...object with id, aud, email, etc...} undefined
false {...object with id, aud, email, etc...} undefined

With no redirect to /signin, so it's definitely just when arriving fresh from the email link. However, it's probably notable that the user object has null for confirmed_at and created_at or any other date values, and userDetails remains undefined the whole time in both page load scenarios.

As a temporary fix, I've started storing the session from onAuthStateChange() with useState(), watching that from the useEffect() and including it in the list of "and not"s before pushing the user to /signin but that feels like it should be unnecessary!

leerob commented 1 year ago

onAuthStateChange is no longer used here, as we've updated things with Supabase's latest improvements. Let us know if you give it another shot 🙏