user() is null on first invocation after Magic Link sign in
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:
- Ideally, make sure that
supabase.auth.user()is synchronous for all cases. - If 1. is not possible, add a new
'NOT_SIGNED_IN'event tosupabase.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
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:
- 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;
- 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);
- 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:
- 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);
- 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.
Is there something new on this issue? Since using supabase i'm facing this issue that the first request for the session is null.
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.
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.
Seems like this is the base issue: https://github.com/supabase/gotrue-js/issues/460
: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:
: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:
This issue has been inactive for a while and I believe the underlying issues have already been addressed with version 2.