It seems to me that there is a problem with the wa...
# javascript
c
It seems to me that there is a problem with the way auth state can be accessed on initial page load.
Assuming that the app uses localStorage to store tokens and 1hr refresh rates, there are 3 possible states when a page is loaded initially: 1) No token is found in localStorage (e.g. user visits the page for the first time on this browser) - this means that supabase.auth.user() will return null and supabase.auth.onAuthStateChanged() will not dispatch an event UNLESS we explicitly call supabase.auth.signIn(); 2) A valid supabase token (i.e. one which is NOT expired) is found in localStorage - this means that a call to supabase.auth.user() will return the current user immediately after the supabase initialization is completed 3) A supabase token is found in localStorage, however, it is expired and needs to be refreshed with the refresh token from local storage - this means that supabase.auth.user() will return null immediated after supabase is initialized but will return non-null once the token is refreshed (assuming the refresh token is valid). The client code can start listening for onAuthStateChanged() immediately after supabase initialization and that way it will know once user is refreshed
The problem I see with this is that there is no way for the client library to differentiate between case (1) and (3) BEFORE any events are dispatched against onAuthStateChanged. If it is case (1), an event will never be dispatched, but the client has no way of knowing this unless the client manually inspects the contents of localStorage (which doesn't seem like a good idea because it uses supabase internal state). Note, that Firebase does NOT seem to suffer the same problem because firebase.auth().onAuthStateChanged is guaranteed to fire at least once after subscribing to it (at least accoring to https://medium.com/firebase-developers/why-is-my-currentuser-null-in-firebase-auth-4701791f74f0). Am I missing something?
i
Hey! I've never used Firebase, so I can't speak to that comparison, and I'm in "make it work and move on" mode right now, so I also haven't dived that far into the internals... But as far as I can tell, this PR https://github.com/supabase/gotrue-js/pull/30 made it so that an onAuthStateChanged event is guaranteed to fire if there's a user in localStorage with fresh (or refreshable) data. It appears to me that this event will fire every time the Supabase client is initialized, because even if user data is present, that data will load in asynchronously. The listener will be listening before the data arrives, and fire upon its arrival.
(This is all in a web context -- I don't know if it would look different natively)
Anyway, the upshot is that for me the
useEffect(()=> {//listen for auth event},[])
pattern discussed in the post you linked earlier and implemented in the UserContextProvider function here (https://github.com/supabase/ui/blob/master/src/components/Auth/UserContext.tsx) works perfectly -- EXCEPT that I believe this implementation is buggy.
I've opened an issue to talk about that here: https://github.com/supabase/ui/issues/291
After making the change I discuss there, I have exactly what I need: 1) A loading state (session === undefined), which is always the state on page load, whether there's fresh data or not. 2) A no-valid-user state (session === null), which only occurs once the client has attempted and failed to recover valid user data. 3) A valid user state (session != null && session != undefined), which occurs when valid data is present or can be fetched. ----- Because that's all working robustly for me now, I suspect that the events you're talking about are firing correctly -- but it's also possible I've missed your point, and there's a different case where they're not working.
c
Thanks for the links @User . I am afraid, I don't understand how any of them solve the issue: 1) The MR you linked does NOT seem to ensure that an event always fires - it is a pretty old MR which doesn't yet include token refresh 2) I don't really understand how introducing the undefined state for the session helps you - maybe I am missing something, I'd love to discuss if you have time over DM, ping me when you see this
j
keep the convo in here if you like, be nice to follow it
c
@User and I got in touch (it was easier to hash it out over a mic) and we looked at his patch (https://github.com/aicushman/ui/commit/3c4e837342f13e4c6c31feecf1d0a0b0eaf054b7), however, I think there is still a problem - it's just harder to hit because it's a race condition
Basically, it depends on whether the token refresh request returns BEFORE or AFTER react gets to update the state of UserContextProvider component (which happens async since it's done via React's setState())
so unless I am wrong about how setState() works or I am missing something else - the problem I describe still exists
d
@User @User were you able to come to any resolution? Is there a solution that is working for either of you?
c
@User My current solution is to manually inspect the contents of localStorage to differentiate between scenarios (1) and (3) I described above (I basically check the 'supabase.auth.token' key in the storage). I think this solves the problem in the short-term, however, I don't think it's a long-term solution because it relies on supabase client internals and NOT on a public API. The issue has been raised in a couple of GitHub issues so, hopefully, there will be a better fix in one of the upcoming releases
d
@chipilov I hope so too 🙂 do you have a snippet of your localStorage-checking code that you’d be willing to share here?
c
sure, here it is:
Copy code
const [pending, setPending] = useState<boolean>(
    supabase.auth.user() != null || localStorage.getItem('supabase.auth.token') != null
);
of course, this assumes that localStorage.getItem is synchronous (this is true for browser context, but I don't think it's true for React Native)
@User Seems like there is ONE more case which is not handled, it's similar to (3) above, but instead of having to refresh a token for an existing user, it's about confirming a newly registered using when they click on the confirmation link sent to their email
Access to supabase.auth.user() on initial load