Skip to content

fix(import): skip re-importing a saved account instead of duplicating it (#59) - #65

Merged
xjoker merged 1 commit into
devfrom
cursor/import-dedup-by-identity-5a91
Aug 26, 2026
Merged

fix(import): skip re-importing a saved account instead of duplicating it (#59)#65
xjoker merged 1 commit into
devfrom
cursor/import-dedup-by-identity-5a91

Conversation

@xjoker

@xjoker xjoker commented Aug 25, 2026

Copy link
Copy Markdown
Owner

Fixes #59.

Problem

import is deliberately create-only. If the incoming auth.json belongs to an account that already has a profile, it still wrote a second one (create_import_profile only de-duplicates on alias-name clashes). Because OpenAI refresh tokens are single-use, the two copies then race: whichever refreshes first (via list or the daemon) rotates the token and the other dies with refresh_token_reused, forcing a full re-login.

It is worse than a duplicate: the usage-service validation that runs during import rotates the token itself, so the new profile is written with a fresh token while the source file — and any existing profile that shared that token — is left holding a dead one.

Fix

Add profile::existing_import_target(source, val) — a read-only check that returns the saved profile these credentials already belong to:

  • byte-identical to a stored profile (find_matching_profile), or
  • the same account_id and email (find_profile_by_identity_exact).

import_one_file consults it before the token-rotating validation and, on a match, skips the file with action "unchanged" (single-file: a clear "already saved … run login to refresh" message; directory: reported as unchanged). No duplicate profile is written and the single-use refresh token is never spent.

Requiring email in addition to account_id means a shared Team account_id never matches a different member, and because the check only ever declines (never overwrites), it cannot hand credentials to the wrong profile — the deliberate create-only safety of save_imported_auth_value is left intact.

Tests

  • profile::existing_import_target_matches_saved_account_and_ignores_others — a rotated-token re-import of a saved account (different bytes) is detected by identity; a genuinely different account returns None.
  • Manual end-to-end (isolated CODEX_SWITCH_HOME): re-importing a saved account prints Already saved as profile 'alice'; skipped … to protect its single-use refresh token, creates no second profile, makes no network call, and exits 0.

Docs

Updated the wiki Feature-Guide.md and Command-Reference.md to describe the skip-on-duplicate behavior.

Verification (Linux)

cargo fmt --check, cargo clippy --all-targets --all-features -- -D warnings, cargo test --all — all pass, 0 failed.

Note: the CI Format and audit job will be red until #63 (webbrowser RUSTSEC-2026-0257 lockfile bump) merges to dev; that failure is pre-existing and unrelated to this change.

Open in Web Open in Cursor 

… it (#59)

import is create-only, so re-importing an already-saved account wrote a
second profile for it. Because OpenAI refresh tokens are single-use, the
two copies then raced: whichever refreshed first rotated the token and the
other died with refresh_token_reused, forcing a full re-login. Worse, the
usage-service validation that runs during import rotates the token itself,
so the duplicate was created with a fresh token while the source file (and
any existing profile sharing that token) was left holding a dead one.

Add profile::existing_import_target: a read-only check that returns the
saved profile these credentials already belong to — byte-identical to a
stored profile, or the same account_id AND email. import now consults it
before the token-rotating validation and skips the file (action
"unchanged") when a match is found, so no duplicate is written and the
single-use token is never spent. The check only declines, never
overwrites, so it cannot hand credentials to the wrong profile and keeps
the deliberate create-only safety of save_imported_auth_value intact.

Docs updated; unit test covers identity match and a distinct-account miss.

Co-authored-by: xJoker <xjoker@users.noreply.github.com>
@xjoker
xjoker marked this pull request as ready for review August 25, 2026 23:43
@xjoker
xjoker merged commit 43ee66a into dev Aug 26, 2026
3 of 4 checks passed
@cursor
cursor Bot deleted the cursor/import-dedup-by-identity-5a91 branch August 26, 2026 07:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants