Problem
The accounts UI/storage still uses tokens.account_id as the unique identity for saved Codex auth snapshots. In ChatGPT Team/Workspace setups, multiple users can share the same tokens.account_id while having distinct user ids and emails. When two users from the same team import or log in on the same machine, codexUI can store them under the same identity, so the later snapshot can overwrite the earlier one and account switch/remove/active restore can target the wrong user.
This was previously addressed in PR #104, but that PR was closed without merge:
#104
Current main still has the same collision path:
src/server/accountRoutes.ts:58-60 stores activeAccountId as the active key.
src/server/accountRoutes.ts:180-182 derives storageId from accountId only.
src/server/accountRoutes.ts:290-295 upserts by accountId, so accounts sharing one account_id replace each other.
src/server/accountRoutes.ts:298-311 sorts and marks active accounts by accountId.
src/server/accountRoutes.ts:700-707 imports snapshots under toStorageId(imported.accountId).
src/api/codexGateway.ts:397-399, src/api/codexGateway.ts:494-501, and src/api/codexGateway.ts:1360-1368 expose/normalize only activeAccountId and accountId for account list state.
src/api/codexGateway.ts:1426-1450 sends switch/remove requests by accountId.
src/App.vue:168, src/App.vue:197, src/App.vue:209, src/App.vue:2465, and src/App.vue:2495 key and route account UI actions through account.accountId.
Impact
Users from the same ChatGPT Team cannot reliably keep separate Codex auth snapshots in codexUI. A second import/login can overwrite the first user's saved auth snapshot. Account switching or active-account restoration can also become ambiguous because account_id is not unique for this scenario.
Expected behavior
When the access token exposes a user id, saved account identity should be derived from both account_id and user_id or another deterministic per-user storage key. Tokens without a user id can keep the legacy account_id fallback.
Legacy state should migrate deterministically so active account restoration never falls back to the first account with a matching accountId when multiple users share that value.
Suggested fix
- Add a stable storage identity separate from display
accountId, for example storageId = hash(account_id + ':' + user_id) when user_id is available.
- Persist and expose
userId/storageId on account records.
- Use
storageId for switch/remove/hover/key UI actions while keeping accountId as display metadata.
- Store
activeStorageId; migrate legacy activeAccountId only when it maps to exactly one stored account or can be resolved from the current ~/.codex/auth.json.
- Keep snapshot import and account-copy helper paths on the same identity rule.
Prior art
PR #104 implemented this direction and later added deterministic migration after the active-account restoration ambiguity was pointed out in review.
Problem
The accounts UI/storage still uses
tokens.account_idas the unique identity for saved Codex auth snapshots. In ChatGPT Team/Workspace setups, multiple users can share the sametokens.account_idwhile having distinct user ids and emails. When two users from the same team import or log in on the same machine, codexUI can store them under the same identity, so the later snapshot can overwrite the earlier one and account switch/remove/active restore can target the wrong user.This was previously addressed in PR #104, but that PR was closed without merge:
#104
Current
mainstill has the same collision path:src/server/accountRoutes.ts:58-60storesactiveAccountIdas the active key.src/server/accountRoutes.ts:180-182derivesstorageIdfromaccountIdonly.src/server/accountRoutes.ts:290-295upserts byaccountId, so accounts sharing oneaccount_idreplace each other.src/server/accountRoutes.ts:298-311sorts and marks active accounts byaccountId.src/server/accountRoutes.ts:700-707imports snapshots undertoStorageId(imported.accountId).src/api/codexGateway.ts:397-399,src/api/codexGateway.ts:494-501, andsrc/api/codexGateway.ts:1360-1368expose/normalize onlyactiveAccountIdandaccountIdfor account list state.src/api/codexGateway.ts:1426-1450sends switch/remove requests byaccountId.src/App.vue:168,src/App.vue:197,src/App.vue:209,src/App.vue:2465, andsrc/App.vue:2495key and route account UI actions throughaccount.accountId.Impact
Users from the same ChatGPT Team cannot reliably keep separate Codex auth snapshots in codexUI. A second import/login can overwrite the first user's saved auth snapshot. Account switching or active-account restoration can also become ambiguous because
account_idis not unique for this scenario.Expected behavior
When the access token exposes a user id, saved account identity should be derived from both
account_idanduser_idor another deterministic per-user storage key. Tokens without a user id can keep the legacyaccount_idfallback.Legacy state should migrate deterministically so active account restoration never falls back to the first account with a matching
accountIdwhen multiple users share that value.Suggested fix
accountId, for examplestorageId = hash(account_id + ':' + user_id)whenuser_idis available.userId/storageIdon account records.storageIdfor switch/remove/hover/key UI actions while keepingaccountIdas display metadata.activeStorageId; migrate legacyactiveAccountIdonly when it maps to exactly one stored account or can be resolved from the current~/.codex/auth.json.Prior art
PR #104 implemented this direction and later added deterministic migration after the active-account restoration ambiguity was pointed out in review.