ecd90b78b9
## Problem A customer reported that after impersonating another user, **their own account's first/last name had been permanently changed to the impersonated user's name** (persisted in the DB, surviving logout), and their actions showed up under the wrong "Updated by". The "Updated by = impersonated user" part is expected (while impersonating you genuinely act as that user). The real bug is the **durable overwrite of the impersonator's profile name**. ## Root cause About a week ago `currentUserState` became localStorage-backed with `getOnInit: true` (`currentWorkspaceMemberState` already was). Impersonation swaps the auth token and does a full reload, but — unlike sign-out — it never cleared those cached identity keys. So after the reload: 1. The atoms hydrate **synchronously from localStorage with the previous identity** (e.g. the impersonated user, on stop), and because `currentUser` is now non-null the authenticated UI renders immediately with that stale identity. 2. The network `loadCurrentUser` then corrects `currentUser` / `currentWorkspaceMember` **in place**. If **Settings → Profile** was mounted across that in-place identity flip, `NameFields` — which seeds local `useState` from `currentWorkspaceMember` once and auto-saves on change — read the stale name as a pending edit and debounce-saved it onto the **now-current** workspace member, persisting one user's name onto another. Read-only caches (object metadata, permissions) tolerate the same staleness because nothing writes them back — they're only ever overwritten by the network. `NameFields` is the one consumer that *persists* a cached identity value, which is what turns a transient stale read into a durable write. ## Fix Two small, complementary layers: - **`useImpersonationSession`** — clear the cached session identity (`clearSessionLocalStorageKeys()`) on both `startImpersonating` and `stopImpersonating`, before the reload. The reload then re-bootstraps from a clean slate for the correct user (and the brief stale-**permissions** flash goes away too). The admin's token stash lives in `sessionStorage` and is untouched; `tokenPairState` has its own key and is not in the cleared set. - **`NameFields`** — re-seed the inputs when the workspace-member **identity** changes, so an identity swap is never mistaken for a user edit. This closes the underlying footgun regardless of how the identity changes. ## Testing - Added `NameFields.test.tsx`: swapping `currentWorkspaceMemberState` to a different member must **not** trigger `updateWorkspaceMemberSettings`, while a genuine user edit still saves. Verified the test **fails without** the `NameFields` fix (it writes the previous member's name onto the new member) and **passes with** it. - `nx typecheck twenty-front`, `oxlint --type-aware`, and `oxfmt --check` all pass on the changed files; full `twenty-front` Jest suite green. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_015MVW3gg7CVq5oR572ctisx --- _Generated by [Claude Code](https://claude.ai/code/session_015MVW3gg7CVq5oR572ctisx)_ <!-- This is an auto-generated description by cubic. --> <a href="https://cubic.dev/pr/twentyhq/twenty/pull/21757?utm_source=github" target="_blank" rel="noopener noreferrer" data-no-image-dialog="true"><picture><source media="(prefers-color-scheme: dark)" srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source media="(prefers-color-scheme: light)" srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img alt="Review in cubic" src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a> <!-- End of auto-generated description by cubic. --> --------- Co-authored-by: Claude <noreply@anthropic.com>