Skip to content

fix(coding-agent): remove plaintext legacy credential files after auth.json migration - #2167

Draft
kevinjosethomas wants to merge 1 commit into
mainfrom
eng-5346-migration-backup
Draft

fix(coding-agent): remove plaintext legacy credential files after auth.json migration#2167
kevinjosethomas wants to merge 1 commit into
mainfrom
eng-5346-migration-backup

Conversation

@kevinjosethomas

@kevinjosethomas kevinjosethomas commented Sep 9, 2026

Copy link
Copy Markdown
Member

Context

migrateAuthToAuthJson moves legacy oauth.json and settings.json apiKeys into auth.json. The destination write is already durable (#2028, unreleased), but after a successful migration oauth.json was renamed to oauth.json.migrated with its original mode (typically 0644) and plaintext access/refresh tokens, nothing ever removed that backup, and when auth.json already existed the migration was skipped so the plaintext legacy sources stayed in place indefinitely.

Root cause: the legacy sources were treated as a backup rather than as plaintext credentials to retire once the destination was verified, and the "destination exists" branch short-circuited instead of merging.

Changes

  • packages/coding-agent/src/migrations.ts
    • After auth.json is written it is re-read and validated to contain every legacy provider; only then is oauth.json deleted (no .migrated backup). Verification failure leaves the sources, chmods oauth.json to 0600 and warns.
    • When auth.json already exists, providers missing from it are merged in and the legacy sources are removed; existing entries are never overwritten. An unreadable auth.json skips the migration and leaves the sources untouched.
    • On every startup a pre-existing oauth.json.migrated is removed once auth.json contains its providers; otherwise it is chmodded to 0600 and a warning is printed.
    • settings.json mode handling is unchanged.
  • packages/coding-agent/test/migrations.test.ts: successful migration leaves no plaintext legacy file; historical .migrated cleanup (verified and unverified); existing-destination merge without overwrite; existing failure test kept.
  • Changelog fragment packages/coding-agent/.changes/eng-5346-migration-backup.md.
  • Docs: no docs mention oauth.json or the .migrated backup (checked packages/coding-agent/docs, READMEs, CHANGELOG), so nothing to update.

Validation

  • npm run check in the worktree: clean (biome, tsgo, installer, browser-smoke).
  • packages/coding-agent: npx tsx ../../node_modules/vitest/dist/cli.js --run test/migrations.test.ts -> 13 passed.
  • Prime Sandbox (node:24-bookworm, unprivileged tester user, synthetic tokens only), git archive of origin/main @ 427ea4c and of this branch @ 4ab2558, HUSKY=0 npm ci in each:
    • Before (main): fresh migration leaves oauth.json.migrated at mode 0644 containing the refresh token; with a pre-existing auth.json the migration is skipped and oauth.json (0644, refresh token) and settings.json apiKeys stay in place; a historical oauth.json.migrated is never touched. The earlier validation fixture (eng5346-migration.test.ts) passes 4/4, i.e. the vulnerable behaviour reproduces.
    • After (branch): fresh migration leaves only auth.json (0600) and the stripped settings.json; the existing-auth.json case merges anthropic/openai next to the existing other entry and removes both legacy sources; a verifiable oauth.json.migrated is deleted; an unverifiable one is chmodded to 0600 with a warning; the read-only-dir write failure still throws EACCES with both sources intact. The vulnerable fixture now fails 3/4 (only the unchanged failure-keeps-sources case passes).
    • Regressions in the sandbox: test/migrations.test.ts 13 passed, test/auth-storage.test.ts 60 passed, test/auth-flows.test.ts 4 passed.

Linear: ENG-5346 — https://linear.app/primeintellect/issue/ENG-5346

Note

Fix migrateAuthToAuthJson to remove plaintext legacy credential files after migration

  • Rewrites migrateAuthToAuthJson in migrations.ts to preserve existing auth.json entries and merge only missing OAuth and API-key providers, instead of early-returning when auth.json exists
  • Deletes oauth.json and strips settings.json apiKeys only after verifying all legacy providers are present in the re-read auth.json; restricts undeleatable files to mode 0600 and warns the user
  • Adds cleanupMigratedOauthBackup to remove historical oauth.json.migrated backups at startup when their providers are already in auth.json; otherwise restricts to 0600 and warns
  • Adds helpers readJsonObjectSync, containsProviders, removeCredentialFileSync, restrictCredentialFileSync, and warnLeftoverCredentialFile to support the migration flow
  • Behavioral Change: migrateAuthToAuthJson now returns only newly added provider names (previously returned all providers); unreadable auth.json is no longer replaced and legacy sources are retained instead

Macroscope summarized 4ab2558.

…h.json migration

Delete oauth.json once auth.json is re-read with every legacy provider,
merge missing providers into an existing auth.json instead of skipping,
and remove or lock down historical oauth.json.migrated backups on startup.

Linear: ENG-5346
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.

1 participant