Firestore not working in beforeAuthStateChanged
Operating System
macOS
Browser Version
125.0.6422.141
Firebase SDK Version
10.12.2
Firebase SDK Product:
Auth, Firestore
Describe your project's tooling
Vite
Describe the problem
Missing Authorization header when requesting firestore inside beforeAuthStateChanged.
NOT WORKING
beforeAuthStateChanged(auth, async (user) => {
if (!user) {
return
}
await auth.authStateReady()
await runTransaction(firestore, async (transaction) => {
const docRef = doc(firestore, 'users', user.uid)
const docSnap = await transaction.get(docRef)
if (!docSnap.exists()) {
transaction.set(docRef, { email: user.email })
}
return
})
})
WORKING
onAuthStateChanged(auth, async (user) => {
if (!user) {
return
}
await auth.authStateReady()
await runTransaction(firestore, async (transaction) => {
const docRef = doc(firestore, 'users', user.uid)
const docSnap = await transaction.get(docRef)
if (!docSnap.exists()) {
transaction.set(docRef, { email: user.email })
}
return
})
})
Steps and code to reproduce issue
// rules
service cloud.firestore {
match /databases/{database}/documents {
match /users/{userId} {
allow create, read, update: if request.auth != null && request.auth.uid == userId;
}
match /{document=**} {
allow read, write: if false;
}
}
}
const auth = getAuth(app)
const firestore = getFirestore(app)
beforeAuthStateChanged(auth, async (user) => {
if (!user) {
return
}
await auth.authStateReady()
await runTransaction(firestore, async (transaction) => {
const docRef = doc(firestore, 'users', user.uid)
const docSnap = await transaction.get(docRef)
if (!docSnap.exists()) {
transaction.set(docRef, { email: user.email })
}
return
})
})
Can you provide more details about your use case and why you need to run this code inside beforeAuthStateChanged instead of in onAuthStateChanged?
I also wouldn't recommend awaiting onAuthStateReady() inside either of those functions. It checks if currentUser exists, and resolves immediately if so, and otherwise calls onAuthStateChanged() and resolves when its callback resolves. This is redundant inside onAuthStateChanged() as it's simply waiting for its own trigger, which has already happened. Inside beforeAuthStateChanged() it also doesn't make sense.
The immediate problem here is that beforeAuthStateChanged() occurs before the user has been written to persistence/memory and also before triggering any listeners waiting to see if auth state has changed, listeners which include not only onAuthStateChanged() but also Firestore's listeners waiting for an auth token. Its main use case is for SSR apps where backend auth state needs to be kept in sync with frontend auth state, and similar cases. At this point, Firestore's listeners have not yet been notified that an auth token is available.
I can't think of a use case where you would need to set an email field that early that couldn't wait until after the user state has been written to persistence/memory. I guess I could see that you might want to check it against allowlisted/blocklisted emails stored on Firestore or elsewhere, and block users from completing login if they're on that list. In any case, whatever your data source is, this all happens before login completes, so it's technically available to anyone and shouldn't be anything security-gated.
Side note: I noticed that beforeAuthStateChanged() does not always run before onAuthStateChanged() - namely it doesn't run before the initial onAuthStateChanged() on page load. It does run on every subsequent login/logout. Should look into if this is intended.
Hey @polRk. 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!
@hsubox76 since beforeAuthStateChanged is a blocking operation—intended for middleware, syncing cookies, etc.—i think it not running unless the token has actually changed (even on page load) is WAI. Ultimately to me it boils down to if you threw in beforeAuthStateChanged on page load you wouldn't be blocking the authStateChanged fire or the passing of the token to the SDKs, since the token is already persisted. In hindsight, we probably should have named it beforeIdTokenChanged
Agree that this issue is not an appropriate use of the API, as Firestore has no auth token to key off of since this is hook is "before" the token is persisted / made available to SDKs
Since there haven't been any recent updates here, I am going to close this issue.
@polRk if you're still experiencing this problem and want to continue the discussion just leave a comment here and we are happy to re-open this.