Skip to content

Signed-in users render as Anonymous; displayName never reaches Firestore #58

Description

@ameyypawar

Signed-in users are rendered as Anonymous on the comments and questions they author. The name is present in Firebase Auth but null in the Firestore users/{uid} document that the UI actually reads, so the || 'Anonymous' fallback fires.

Reproduced on the live site: a comment posted by a signed-in account whose Firebase Auth displayName is correctly set renders as "Anonymous", directly beneath older comments that render their author's name normally.

Evidence

Comparing Firebase Auth against the Firestore mirror for the same accounts:

Source Value
Firebase Auth displayName set correctly for every account
Firestore users/{uid}.displayName null for 6 of 10 accounts

The Auth records all carry real names. The Firestore documents that the UI reads do not. Four accounts happen to have a correct displayName; the remaining six are null. One further account — the oldest, an admin — has no displayName at all and instead carries a legacy name field, matching the older schema also seen in #55.

Root cause: a race between onAuthStateChanged and signUp

src/contexts/AuthContext.tsxsignUp runs this sequence:

const result = await createUserWithEmailAndPassword(auth, email, password);  // 1
const firebaseUser = result.user;
await updateProfile(firebaseUser, { displayName });                          // 2
await sendEmailVerification(firebaseUser);
const newUserProfile: UserProfile = {
  uid: firebaseUser.uid, email: firebaseUser.email,
  displayName: displayName, photoURL: firebaseUser.photoURL,
};
await createUserProfile(newUserProfile);                                     // 3

Step 1 signs the user in, which immediately fires the onAuthStateChanged observer registered earlier in the same file. That observer calls loadUserProfile(firebaseUser), which finds no Firestore document and creates one itself:

// AuthContext.tsx — inside loadUserProfile
const newUserProfile: UserProfile = {
  uid: firebaseUser.uid,
  email: firebaseUser.email,
  displayName: firebaseUser.displayName,   // still null — step 2 has not run yet
  photoURL: firebaseUser.photoURL,
};
await createUserProfile(newUserProfile);

firebaseUser.displayName is null at that instant, because updateProfile (step 2) has not executed. So the document is created with displayName: null.

Step 3 then attempts to write the correct value, but createUserProfile is guarded on non-existence:

// src/lib/services/userService.ts
const userDoc = await userRef.get();
if (!userDoc.exists) {
  await userRef.set({ /* ... displayName: user.displayName ... */ });
}

The document now exists, so this is a silent no-op and the correct name is discarded. Whichever path wins the race determines whether the account has a name — which is why the outcome is split across existing accounts rather than uniformly broken.

Why a profile fix alone is not sufficient

Author details are denormalized into every question and comment at write time (questionService.addQuestion, commentService.addComment, eventService.addEvent all persist the whole author object — see #23). Content authored while displayName was null is permanently stamped with null; correcting the user document later does not retroactively fix existing posts.

A complete fix therefore has three parts:

  1. Fix the race. Either set the Auth profile before the observer can create the document, or make createUserProfile fill in a missing displayName on an existing document instead of returning early. The second is more robust, since it repairs the state regardless of ordering.
  2. Backfill users/{uid}.displayName from Firebase Auth. The correct values already exist in the Auth records, so this is a straightforward one-off script over listUsers().
  3. Backfill the denormalized author.displayName on existing questions, events, and questions/{id}/comments documents — or implement Stop denormalizing full UserProfile into question/comment/event docs #23 (store an author reference and resolve display fields at read time), which repairs all history at once and prevents the class of problem recurring.

Also worth trimming input on the way in: at least one Auth record has a trailing space in its displayName, so the sign-up form is not trimming.

Notes

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions