Account settings: change avatar, username, email, and password - #5
Open
notAidven wants to merge 1 commit into
Open
Account settings: change avatar, username, email, and password#5notAidven wants to merge 1 commit into
notAidven wants to merge 1 commit into
Conversation
Add a cohesive "Account settings" section to the profile with inline
edit + validation + loading + success/error feedback for four things,
all on the client Firebase Auth + Firestore SDKs (no Cloud Functions):
- Avatar: reuse the poker AnimalPicker, write profileAnimal; reflects in
the header + profile immediately via refreshProfile.
- Username: same validation + uniqueness as signup. Atomically release
usernames/{old} and claim usernames/{new} in one transaction and rename
users/{uid}.username; taken names are rejected.
- Email: verifyBeforeUpdateEmail sends a confirm link to the NEW address
(email changes only after they click it); reconcile users/{uid}.email +
usernames lookup on next load. Re-auth on auth/requires-recent-login.
- Password: updatePassword for password users (ask current + reauth);
Google-only users reuse the existing SetPasswordCard/linkEmailPassword
flow (no duplication).
Add one shared reauthenticate() helper on AuthContext (password credential
or Google popup) used by both the email + password changes, with friendly
messages via authErrors.ts (new 'reauth' context).
firestore.rules (NOT deployed - maintainer must deploy): allow the owner to
change users/{uid}.username, set users/{uid}.email only to their verified
token email, delete a usernames doc they own, and update a usernames doc's
email to their own verified token email.
Co-authored-by: Cursor <cursoragent@cursor.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Stacked on #2 (
feature/auth-google-email-link). Adds a cohesive Account settings section to the profile page so a signed-in user can change their avatar, username, email, and password. Each row has inline edit + validation + loading state + success/error feedback.Zero Cloud Functions / no Blaze required — everything runs on the client Firebase Auth + Firestore SDKs (free tier).
Capabilities
AnimalPicker; saving writesprofileAnimaltousers/{uid}and callsrefreshProfile()so the header avatar + profile update immediately.validateUsername3–20 chars[a-zA-Z0-9_], lowercased index key,usernames/{name}uniqueness). The rename runs in one Firestore transaction: it checksusernames/{new}is free, deletes the oldusernames/{old}, claimsusernames/{new}({uid, email}), and renamesusers/{uid}.username— so a name is never double-claimed and the old one is never orphaned. Taken names are rejected with a clear message. A case-only edit (e.g.maya→Maya) just updates the display name (same index key, so no release/claim).verifyBeforeUpdateEmail, which sends a confirmation link to the new address; the account email only changes after the user opens that link. The UI states this clearly and tells them to keep using their current email until then. On the next load (fresh token), the app reconcilesusers/{uid}.emailand theusernameslookup doc to the new lowercased address so username + password sign-in keeps working.updatePasswordafter asking for the current password and re-authenticating first.needsPasswordSetup): the password row delegates to the existingSetPasswordCard/linkEmailPasswordflow (scrolls to + focuses it) instead of duplicating it.Shared re-authentication flow
One reusable
reauthenticate(password?)helper onAuthContext, used by both the email and password changes:reauthenticateWithCredential(EmailAuthProvider.credential(...))reauthenticateWithPopup(google)Email change triggers it on
auth/requires-recent-login(then retries automatically via the sharedReauthPrompt); password change always re-auths up front. Friendly messages route throughauthErrors.ts, including a new'reauth'context so a wrong current password reads "Incorrect password." (not the login-style "username or password"). Handlesauth/wrong-password,auth/invalid-credential,auth/email-already-in-use,auth/popup-closed-by-user,auth/requires-recent-login, etc.firestore.rules change — NOT deployed (maintainer must deploy)
firestore.rulesis updated but not deployed; please deploy it for the feature to work in production. All changes stay tightly scoped + secure:users/{uid}update:emailmay only be set to the caller's verifiedrequest.auth.token.email(or left unchanged);usernamemay change to any string (uniqueness/ownership enforced by theusernamesindex). Field whitelist + gamification validators are unchanged.usernames/{name}:createunchanged (must not exist, own uid + token email); newdeleteonly if the doc points to your own uid; newupdateonly by the owner, uid immutable, email pinned to your own verified token email. A user can never point a name at an address they have not proven they own, nor touch another user's doc.UX / a11y
aria-expanded/aria-controlsdisclosure toggles,role="alert"errors +role="status"success, focus moved to the first field on open.SetPasswordCardor the set-password banner.Verification (from
web/)npm run build→ exit 0npx vitest run→ 64 passed (added tests for the email/password validators + thereautherror context)node_modules/.bin/tsc -p tsconfig.app.json --noEmit→ cleannode scripts/mvp-logic-check.mjs→ all PASSnpm run lint→ no new errors introduced (pre-existingreact-hooks/react-refreshfindings only)Do not auto-merge.
Made with Cursor