Open shaibt opened 1 year ago
Thanks for reporting this. I tried modifying the demo app in and could not repro this issue. I added signInAnonymously
in https://github.com/firebase/firebase-js-sdk/blob/547348ba59e1a2bf108330bd882c2ea2753d54d0/packages/auth/demo/src/index.js#L1749 and it returns the same user once signed in.
We do await on the initializationPromise here - https://github.com/firebase/firebase-js-sdk/blob/cdada6c68f9740d13dd6674bcb658e28e68253b6/packages/auth/src/core/strategies/anonymous.ts#L38, so the same issue in #6827 shouldn't happen. Also, the fix for that issue was included in 9.16.0 - https://github.com/firebase/firebase-js-sdk/pull/6953, are you able to try with that? (though i am not sure it is related).
2 more followups:
1) in onAuthStateChanged callback are you able to log the uid and the sign_in_provider or is_anonymous fields (to make sure the different uids are all part of anonymous sign in only)?
2) Are you able to reproduce this in other browsers, or only Safari/iOS?
Hi @prameshj,
I think the scenario for this issue is a little different than originally described. First, to your questions and then an update:
onAuthStateChanged
callback we started to log the uid
we receive in the last couple of weeks and I have some more insight on this below. We'll add the isAnonymous
and providerId
data as well to our logs going forward. We will also upgrade firebase 9.15.0 -> 9.17.2
Update:
Looking at logs from last couple of weeks since we opened this issue including logging for onAuthStateChanged
callback it looks like the affected sessions are suffering from the following issue:
onAuthStateChanged
fired with uid==undefined
signInAnonymously
called. onAuthStateChanged
fired with uid==123
signInWithCustomToken
called. onAuthStateChanged
fired with uid==123
(same as before as expected). The session in this case is no longer anonymous.signInWithCustomToken
is called - onAuthStateChanged
is fired without any auth operation by the user with uid==undefined
so we call signInAnonymously
again and callback is fired with uid == 456
(diff than before). Seems that at some point Firebase Auth decides to reset the auth data but the client is already signed in!. How can this happen without an explicit signout? Could it be that in case of Safari/Webkit Auth loses some persisted data that causes this "refresh" ? An important point I forgot to mention in the OP is that our Firebase app opens as an iFrame over a "hosting" website so it'll be considered a 3rd party domain by the browser.
Could it be that Auth has difficulty persisting data in this case of Safari/webkit on cross-domain and spontaneously refreshes the uid
?
uid
as described in the original issue post. This might be a separate issue or part of the same one.As to browsers/devices:
Seems that at some point Firebase Auth decides to reset the auth data but the client is already signed in!.
This is very weird - Does this happen while they are on the web app still, i.e no page refresh?
It is possible that there is some cross-origin storage access at play here. So, the hosting website is domain1. Your firebase app is domain2 and in domain1, you open an iframe to domain2 (where the onAuthStateChanged callbacks are invoked), correct?
Are you using Local Storage persistence by any chance? I wonder if this is what happens:
1) https://github.com/firebase/firebase-js-sdk/blob/0b3ca78eb97ce328b7db4ad5bb11f254e5de6a9a/packages/auth/src/core/persistence/persistence_user_manager.ts#L60 - we setup a listener for changes to storage 2) The local storage entry for the user somehow gets wiped out and we set current user to null - https://github.com/firebase/firebase-js-sdk/blob/0b3ca78eb97ce328b7db4ad5bb11f254e5de6a9a/packages/auth/src/core/auth/auth_impl.ts#L180, which invokes onAuthStateChanged callback.
There is some special case in Local Storage persistence for Safari/iOS + iframe - https://github.com/firebase/firebase-js-sdk/blob/7d23aa4bd1e29d2c10c771c0ab7919b6c5dd2d9b/packages/auth/src/platform_browser/persistence/local_storage.ts#L90, but I think this is related to syncing the local storage manually (rather than items getting wiped out). Curious if you are using a specific persistence setting though. cc @sam-gc
Hey @shaibt. We need more information to resolve this issue but there hasn't been an update in 5 weekdays. I'm marking the issue as stale and if there are no new updates in the next 5 days I will close it automatically.
If you have more information that will help us get to the bottom of this, just add a comment!
Hey @prameshj:
It is possible that there is some cross-origin storage access at play here. So, the hosting website is domain1. Your firebase app is domain2 and in domain1, you open an iframe to domain2 (where the onAuthStateChanged callbacks are invoked), correct?
You are correct - the Firebase app is only contained inside the iframe for domain2.
Are you using Local Storage persistence by any chance? I wonder if this is what happens:
We don't set any special config settings during Firebase (or Firebase Auth) init - just plain default settings. What is the default persistence method? Does Firebase auth persist data in local storage?
In our app, we do use local storage for persisting several app variables and indeed in Safari they don't get retained for very long - not longer than a few hours. Seems that Safari in this cross domain configuration will clean the cross domain iframe's local storage from time-to-time and we can see this as the variables are not persisted over long periods of time as in Chrome.
However, the only Firebase related entry in local storage that I can see is firebase:host:[project-name].firebaseio.com
and nothing to do with the current auth session.
Could it be that the code you referenced is invoked when Safari decides to wipe storage and that causes Auth to unnecessarily reset the session?
We don't set any special config settings during Firebase (or Firebase Auth) init - just plain default settings. What is the default persistence method? Does Firebase auth persist data in local storage?
With getAuth
, we pass in indexDb, localStorage, sessionStorage. https://github.com/firebase/firebase-js-sdk/blob/fdd4ab464b59a107bdcc195df3f01e32efd89ed4/packages/auth/src/platform_browser/index.ts#L82
If indexDb is not available, it falls back to local storage. https://github.com/firebase/firebase-js-sdk/blob/fdd4ab464b59a107bdcc195df3f01e32efd89ed4/packages/auth/src/core/persistence/persistence_user_manager.ts#L116-L133
Is it possible that indexDB is not available in the Safari version you are using? Do you see any indexDB/local storage entry like:
firebase:authUser:<api key>:[DEFAULT]
Could it be that the code you referenced is invoked when Safari decides to wipe storage and that causes Auth to unnecessarily reset the session?
The code I reference will get invoked upon local storage events, so if Safari does wipeout the storage entry, that will explain the issue you see.
In Safari dev tools I cannot see the firebase:authUser:<api key>:[DEFAULT]
in neither local storage or indexedDB.
I wasn't sure if its browser policy or the dev tools only showing indexedDB for the main/hosting domain1 and not showing for the Firbebase app's domain2. So I added some code to our firebase app to open the IndexedDB named firebaseLocalStorageDb
inside the iframe/domain2 and indeed the firebase:authUser...
key was there. So it is persisted in IndexedDB.
So if auth is persisted in indexedDB why is a local storage event refreshing auth?
Or more likely, Safari is also clearing the IndxedDB which would cause the onAuthStateChanged
to fire with user null
as well.
PS: In Chrome, under same exact scenario I can clearly see the auth entry in indexedDB under domain2.
Hey @prameshj - any updates/ideas of how we can work around this Safari issue?
I wasn't sure if its browser policy or the dev tools only showing indexedDB for the main/hosting domain1 and not showing for the Firbebase app's domain2.
This is likely the case, Safari 16.1+ does session storage partitioning, it probably extends to local and indexedDB storage too..
So if auth is persisted in indexedDB why is a local storage event refreshing auth? Or more likely, Safari is also clearing the IndexedDB which would cause the onAuthStateChanged to fire with user null as well.
yea, if you are using indexedDB, then the local storage code path I pointed out is likely not suspect. Are you seeing indexedDb entries in the same Safari version that sometimes sees the race condition issue? I wonder if the affected versions somehow don't have indexDb available. Or, as you mentioned, maybe indexedDb entries are getting wiped out too. Are you able to add a listener for indexedDb entries and see if those fire?
Cannot inspect indexedDB in dev tools because for some reason Safari will only show the main domain's indexedDB. But when using code to read the contents of the indexedDB in the iframe I can see the Firebase auth entries there. I'm not sure its possible to set listeners to IndexedDB - or maybe I didn't understand your last comment.
You're right.. looks like indexedDb doesn't have storage event observer support - https://stackoverflow.com/questions/33237863/get-notified-when-indexeddb-entry-gets-changed-in-other-tab
It is possible to poll.. which is what we do in the auth persistence implementation - https://github.com/firebase/firebase-js-sdk/blob/7d23aa4bd1e29d2c10c771c0ab7919b6c5dd2d9b/packages/auth/src/platform_browser/persistence/indexed_db.ts#L354
Which Safari version are you seeing the issue with?
Also, as an aside, how do you handle the cases where a user logouts from the app? That will still call onAuthStateChanged and create a new anonymous user. That wouldn't cause this race condition (unless there is some sign out code path triggering erroneously), but it would lead to a lot of zombie anonymous users (if the user closes the page shortly after signing out).
Seeing it across all Safari 16.X versions in iOS & OSX.
There is no logout option for the app. To keep things simple:
signInAnonymously
and then ask our backend to create a custom sign in token for that uid
(the custom token includes some custom claims used in our backend and DB rules) and call signInWithCustomToken
.uid
-> this is what's causing the issue.uid
might change.Thanks for the additional info! We will try to repro this at our end with additional logs.
Hi @prameshj, just wondering if you have any update on this case.
We haven't been able to get a repro yet. cc @jbalidiong
I had the same problem with Safari 17.1.2. The code is slightly different because it uses Angularfire, but I believe the issues are the same. This issues will not occur unless perhaps the old credentials are still in indexedDb.
problem code
export class FirebaseService {
constructor(
private afa: AngularFireAuth,
) {}
public initializeFirebase(): void {
this.afa.authState.subscribe(user => {
// **not get here**
});
this.afaLogin();
}
public afaLogin(): void {
this.afa.signInAnonymously().catch(()=> {
setTimeout(() => {
this.afaLogin();
}, 1000);
});
}
}
modified code
export class FirebaseService {
constructor(
private afa: AngularFireAuth,
) {}
public initializeFirebase(): void {
this.afa.signInAnonymously().then(() => {
this.afa.authState.subscribe(user => {
// **get here**
});
}).catch(()=> {
setTimeout(() => {
this.initializeFirebase();
}, 1000);
});
}
}
Hi @prameshj,
Bumping this issue as we're still plagued with problems in production - Safari browsers only (ver 16.X, 17.X)
Just a quick reminder:
Launch Firebase auth inside a cross domain iframe - hosting site origin XXX.com
and iframe w/ Firebase origin is YYY.com
.
Initial auth is fine and onAuthStateChanged
is fired with a valid user.
But at some random timing in the future, with no user interaction, onAuthStateChanged
is fired again with null
user.
Seems Safari is wiping out the storage.DB used for persisting credentials and casuing auth state to reset while app is in flight.
Any plans to address this?
Looks like its remotely related to this issue?
[REQUIRED] Describe your environment
[REQUIRED] Describe the problem
Steps to reproduce:
On loading, our React app immediately loads a component that does the following:
getAuth()
onAuthStateChanged
onAuthStateChanged
handler is called for 1st time we check for a valid user objectsignInAnonymously
What we're observing in our production env on an irregular basis is that the
onAuthStateChanged
is called multiple times with different auid
everytime.More specifically, the 1st time app is opened on a "clean" browser, there is no persisted user
uid
for Firebase auth so theonAuthStateChanged
handler fires withnull
user, we performsignInAnonymously
, andonAuthStateChanged
is fired again with validuid
. Next time app is opened (few mins later) we can seeonAuthStateChanged
fired mutliple times in sequence with an alternatinguid
provided - one time the previousuid
from the first-time open and a newuid
.Expectation is that the previous session's
uid
will be provided OR that a newuid
be created but would not change -onAuthStateChanged
should not be called with multiple diff values ofuid
for the same browser session.Relevant Code:
This wasn't reproducible in testing envs and happens only on occasion on production. Could it be that calling
getAuth()
multiple times would create more than 1 instance of the auth service? Could it be that callingsignInAnonymously
too early would cause a race condition in auth service to generate twouid
for same browser? This issue looks similar in some ways to the issue described in https://github.com/firebase/firebase-js-sdk/issues/6827