Well I've solved my actual problem by setting a timestamp on the object via another firestore listener, then comparing the current and previous to only trigger once.
I'll post this code if anyone comes here doing a similar thing, but I'd still want to find out why my subscriber is going off so many times.
Cloud function:
export class UserUpdateListener { public listen = functions.firestore .document('user/{uid}') .onWrite(async (snapshot) => { const before: User = snapshot.before.data() as User; const after: User = snapshot.after.data() as User; const skipUpdate = before.lastUpdate && after.lastUpdate && !before.lastUpdate.isEqual(after.lastUpdate); if (skipUpdate) { functions.logger.info('No changes, skipping timestamp update'); return; } await snapshot.after.ref.update({ lastUpdate: admin.firestore.FieldValue.serverTimestamp() }); });}
I then check the timestamp in my client using:
constructor(private angularFireAuth: AngularFireAuth, private firestore: AngularFirestore, private router: Router) { this.onAuthStateChanged(); let lastUpdate: Timestamp; this.user$.subscribe(async (user) => { if (this.authUser && user && user.lastUpdate) { if (lastUpdate && !user.lastUpdate.isEqual(lastUpdate)) { await this.authUser.getIdToken(true); } lastUpdate = user.lastUpdate; } });}
Hopefully this will save someone some time.