-
Notifications
You must be signed in to change notification settings - Fork 938
FR: Change behavior of onAuthStateChanged. #4133
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
I'm not sure I understand what the request is. Is the problem the same as the one you are asking about in the comment here: #462 (comment)? If so, does the answer there solve your problem? Or is this a separate issue? |
Yes, this is the same problem. I tried your solution, and they didn't help because if a user wants to get access to the private route, then the router will wait until verification competes and the user will see a white screen all time. If I remove check verification from the route, then route redirects the user to a login page. Because I need the auth call callback less than 100ms even they can't get information from google. |
@hsubox76 I think I have a similar use case, although it's a multi-page application instead. Whenever the user navigates to a new page, everything including Firebase is initialized from scratch. I have some JS code that will dynamically render the page by setting <header>
<script type="module">
onAuthStateChanged(getAuth(), user => {
document.body.innerHTML = `Hello, ${user.displayName}!`
})
<script>
</header>
<body></body> Even on a fast connection, it takes 300ms for the callback to be called, so the browser won't wait and will show the blank page (because the body is empty in the HTML itself). So this is what the user sees in the screen:
On the other hand, if
Summarizing: if |
I found that this is where the problem happens: firebase-js-sdk/packages/auth/src/core/user/reload.ts Lines 31 to 75 in cdada6c
Firebase indeed has the auth state stored locally (the Instead, I think it could immediately notify the listeners with the cached auth state and THEN proceed to check if the invalidation is actually needed (most times it won't be, at least for my app). I understand that for some apps the invalidation may be important for security, but if that's the case, then at least add an option to Please consider following my suggestion. It's a low-hanging fruit that will make all MPAs using Firebase Auth a lot faster. I'll even leave a draft diff here so you have something to start with 😊: export async function _reloadWithoutSaving(user: UserInternal): Promise<void> {
const auth = user.auth;
+
+ // Notify listeners with the cached user before checking if the invalidation is needed
+ // so the app can initialize fast.
+ user._notifyReloadListener(user);
+
+ // Wrap in a promise that's not awaited so it doesn't block the function termination.
+ Promise.resolve(async () => {
const idToken = await user.getIdToken();
const response = await _logoutIfInvalidated(
user,
getAccountInfo(auth, { idToken })
); I know this diff would break other places using the |
Hi @gustavopch, thanks for digging into this more! My main concern is that this proposal blurs the lines between With that said, there may be a way to deliver this functionality using a different/new API. I will discuss this more closely with the team. |
@sam-gc That makes sense. I'll explain my actual use case. Pseudo-code: let unsubscribe
onAuthStateChanged(getAuth(), authState => {
unsubscribe?.()
unsubscribe = onSnapshot(
doc(getFirestore(), `users/${authState.uid}`)
user => { console.log('User loaded') },
)
}) As you can see, I can only add the Firestore listener once I know the ID of the current user, so even when I have Firestore persistence enabled, it won't load the user document as fast as it could. I'm also not sure if there's any code inside Firestore that would wait for the auth state to be known even if my own code wasn't wrapped in an |
@sam-gc Well, maybe simply populate let unsubscribe
const handleAuthState = authState => {
unsubscribe?.()
unsubscribe = onSnapshot(
doc(getFirestore(), `users/${authState.uid}`)
user => { console.log('User loaded') },
)
}
handleAuthState(getAuth().currentUser)
onAuthStateChanged(getAuth(), handleAuthState) Note: added |
The problem with either is that it changes the way the API operates, which is a breaking change. The team is exploring other options that avoid changing existing functionality (i.e. by adding this as a new feature). One thing to note is that if the token is too old, the database read will fail on the server with a permission denied error. I'm not sure how this will interact with the local cache |
@sam-gc Here's how I think it would work:
Assuming most times the token can be refreshed, then most times it will work seamlessly. In cases where the token can't be refreshed, then the user may see the cached data for a moment and then be forced to re-authenticate. Of course, that's assuming the developer wants to use the cached data, but it would be optional. |
Hi, any movement on this? @sam-gc
Wholly agreed here. Right now, the predominant workaround is to store some value in local storage (eg, a boolean, or maybe even the entire 99.99% of the time, the user will be successfully validated, and only in 0.01% of the time might the user be deleted, or credentials revoked. We should optimise for the common case instead of the rare one.
I don't see how pre-populating the user with the stored data from IndexDB changes the way the API operates. The Edit: even today, |
I'm running into this issue as well.
In my experience, if a device is offline, then the request to refresh the user will fail fast and the app will launch quickly. So IMO it's great that the cache exists, and is certainly not just there as an optimization. However, when on a slow network (such as a 3G connection), the refresh request has to time out before anything else can proceed, which is frustrating. I'm currently hacking around this problem by intercepting It'd be great if the initialization sequence didn't block on this refresh, or if there were some other way to fetch the current user object from local storage while the initialization progresses. I don't really want to add my own user cache on top of the one that Firebase Auth provides, since that presents a bunch of additional synchronization issues to manage. |
I just wanted to add that other than the slow 3G connection use case, anyone in a region where Firebase Auth isn't replicated to will also feel a lot of pain. My customers are primarily in Australia, and the 300-600ms latency makes every single page refresh really clunky feeling, because I can't render anything if I don't know if the user is logged in or not. |
@sam-gc Hello, friendly bump - any movement or new information on this issue? |
This is a really annoying bug in Firebase. I've been trying to dig though the firebase sdk to see how to fix this, but it's pretty esoteric Looking in the Chrome stack trace, I think onSnapshot waits for this auth call to complete before continuing to load: firebase-js-sdk/packages/firestore/src/remote/persistent_stream.ts Lines 446 to 461 in 086df7c
|
Any update on this? this was raised on 2020 and still no progress what so ever. This is killing the whole firebase ecosystem for MPA. |
There does not appear to be a fix for this issue. I'm working with a react native app which is also web enabled. I can confirm react-native-firebase does not have the same issue, I'm assuming the currentuser details are stored locally to allow for offline access. Like most apps, I initally load the Sign In screen and wait for onAuthStateChanged to be triggered. If a user is logged in and refreshes the page, then even running on a fast network with no network delays, I'm finding two issues:
I can provide the a simple app if that is required so you can replicate the issue. The proposals above make perfect sense. I guess the only workaround is for apps where user convenience is important, the workaround for now is:
The problem with this approach is onAuthStateChanged has to be triggered before auth will respond to sign in\out requests. Sorry firebase auth team, this all needs either a serious rethink and proper solution or in case we're all getting this wrong, a real working example. Finally, I did try using: import ReactNativeAsyncStorage from '@react-native-async-storage/async-storage'; auth = initializeAuth(app, { As documented in the react-native-firebase documentation, however; there are issues with modules not existing in packages and I wasn't able to get any combination of the above working. Looking forward to a fix from the firebase team. Thanks. |
[REQUIRED] Describe your environment
Problem:
If a user was logged and try to open spa app on page wich required auth access the app should waiting until auth will be confirmed and only then call callback
onAuthStateChanged
with the user object. This check can be quite long (more than 5 seconds with slow 3g).Propose:
Call callback when we find user information in storage with this user object without waiting for the end of the auth check. If auth check return that user was logout than call callback with user null.
Thanks!
The text was updated successfully, but these errors were encountered: