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

user() is null on first invocation after Magic Link sign in

Open joanllenas opened this issue 4 years ago • 7 comments

Bug report

Describe the bug

When you sign in via Magic Link from an email, the user is null initially. In order to successfully initialize my app I have to use a setTimeout:

if (supabase.auth.user()) {
  initializeApp(supabase);
} else {
  setTimeout(() => initializeApp(supabase), 1000);
}

To Reproduce

const supabase = createClient(supabaseUrl, supabaseAnonKey);
console.log("supabase.auth.token", localStorage.getItem("supabase.auth.token")); // null
console.log("User", supabase.auth.user()); // null
supabase.auth.onAuthStateChange((evt, session) => {
  console.log("onAuthStateChange.evt", evt); // 'SIGNED_IN'
  console.log("onAuthStateChange.session", session); // { access_token, user, (...) }
});

Expected behavior

I would say that there are at least two possible expected behaviours:

  1. Ideally, make sure that supabase.auth.user() is synchronous for all cases.
  2. If 1. is not possible, add a new 'NOT_SIGNED_IN' event to supabase.auth.onAuthStateChange.

System information

  • OS: macOS
  • Browser chrome 94.0.4606.71
  • Version of supabase-js: 1.24.0
  • Version of Node.js: 14.17.6

Additional context

Related to https://github.com/supabase/gotrue-js/issues/23

joanllenas avatar Oct 08 '21 14:10 joanllenas

I think the problem goes deeper than this and doesn't only affect magic links.

The issue is that on initial page load, there are 3 possible scenarios:

  1. There is no token stored in local storage - in this case, supabase.auth.user() will return null immediately after calling createClient() and will stay null until the user logs in explicitly;
  2. There is a token stored in local storage and it has NOT expired - in this case, supabase.auth.user() will return non-null immediately after calling createClient() (as long as localStorage API used is synchronous, which is the case for browsers);
  3. a) There is a token stored in local storage, however, it is expired and needs to be refreshed BEFORE the user is considered authenticated - in this case, supabase.auth.user() will return null immediately after calling createClient(), however, as soon as the request for refreshing the token succeeds, onAuthStateChanged handlers will be invoked and supabase.auth.user() will return non-null. b) The user has clicked on the confirmation e-mail after registration - in this case, supabase.auth.user() will return null immediately after calling createClient(), however, as soon as the user's access token from the confirmation link is verified (via a REST call), the user will bed signed-in.

Unless I am missing something, there is no way for a developer to differentiate between (1) and (3) if supabase.auth.user() returns null. If it is (3) then you don't want to redirect the user to a login page because you just need to wait a bit until their token is refreshed (i.e. wait until onAuthStateChanged() is fired). If it is (1), however, you do want to redirect them.

It seems to me that the only way to currently know what to do is to manually inspect local storage to see if there is a token inside. However, this does NOT seem like a good option since it relies on the internals of the GoTrue client.

Regarding @joanllenas 's suggestions above:

  1. Making user() by synchronous always doesn't seem possible because of the scenario where a token is present in localStorage but needs to be refreshed. Also, I am not sure if this is possible even for non-expired tokens in environments whereh localStorage is async (e.g. react-native, as far as I know);
  2. This seems like an option but ONLY if it is guaranteed that a subscriber will receive at least one event after subscription. Otherwise, with the current implementation, the event will most likely be dispatched BEFORE the user has had a chance to subscribe for it.

chipilov avatar Nov 16 '21 13:11 chipilov

Is there something new on this issue? Since using supabase i'm facing this issue that the first request for the session is null.

megacherry avatar Dec 07 '21 14:12 megacherry

This is also an issue for me with Facebook. I think if gotrue just opened the OAuth provider in another tab it would work.

The redirects by the providers break the OAuth State Listener.

linoadmin avatar Jan 11 '22 22:01 linoadmin

Here are some instances of people complaining about this issue on the web.

  • https://www.reddit.com/r/Supabase/comments/oqlg85/supabaseauthuser_returns_null_even_after_login/
  • https://github.com/supabase/gotrue-js/issues/401
  • https://www.reddit.com/r/Supabase/comments/oye0hy/how_are_you_guys_checking_if_a_user_is_logged_in/
  • https://stackoverflow.com/questions/68332321/supabase-third-party-oauth-providers-are-returning-null

I'm sure I could find more, but I hope this highlights that this is a long ongoing issue.

linoadmin avatar Jan 12 '22 02:01 linoadmin

Seems like this is the base issue: https://github.com/supabase/gotrue-js/issues/460

kungpaogao avatar Jan 19 '22 20:01 kungpaogao

:tada: This issue has been resolved in version 1.23.0-next.1 :tada:

The release is available on:

Your semantic-release bot :package::rocket:

github-actions[bot] avatar Jun 07 '22 01:06 github-actions[bot]

:tada: This issue has been resolved in version 2.0.0-rc.1 :tada:

The release is available on:

Your semantic-release bot :package::rocket:

github-actions[bot] avatar Aug 16 '22 14:08 github-actions[bot]

This issue has been inactive for a while and I believe the underlying issues have already been addressed with version 2.

hf avatar Dec 30 '22 16:12 hf