Skip to content

fix: auth credential storage and transient error handling#81

Merged
nicknisi merged 6 commits into
mainfrom
nicknisi/auth-fixes
Mar 5, 2026
Merged

fix: auth credential storage and transient error handling#81
nicknisi merged 6 commits into
mainfrom
nicknisi/auth-fixes

Conversation

@nicknisi
Copy link
Copy Markdown
Member

@nicknisi nicknisi commented Mar 5, 2026

Summary

  • Config store: stop deleting file after keychain writeconfig-store.ts was deleting ~/.workos/config.json after successful keychain migration, causing environment configs to vanish when keychain became temporarily inaccessible (e.g., native addon rebuild). File is now preserved as a fallback.
  • Credential store: make keychain-primarycredential-store.ts was always writing to file alongside keychain. Now only writes file when keychain fails, matching the intended keychain-primary pattern.
  • Don't clear credentials on transient refresh errorsensure-auth.ts was calling clearCredentials() on network/server errors during token refresh, destroying a valid refresh token over temporary hiccups. Now only clears on invalid_grant (permanently dead refresh token).

Test plan

  • saveConfig writes to keyring only when keyring available (no file created)
  • saveConfig falls back to file when keyring unavailable
  • Existing config file NOT deleted on subsequent keyring success
  • saveCredentials same keychain-primary behavior
  • getConfig migration to keyring preserves file backup
  • Transient refresh failure preserves credentials for retry
  • invalid_grant still clears credentials (correct behavior)
  • All 988 existing + new tests pass
  • Typecheck and build pass

nicknisi added 6 commits March 5, 2026 10:39
…lback

Both stores now follow the same pattern: write to system keychain first,
only write to file when keychain is unavailable, never proactively delete
existing file backups.

config-store: removed deleteFile() calls from saveConfig() and getConfig()
migration path that caused environment configs to vanish after addon
rebuilds or keychain access failures.

credential-store: stopped always writing to file alongside keychain.
File is now only written as fallback when keychain write fails.
Prevents race condition where two concurrent CLI processes both attempt
to refresh the same token, causing one to get invalid_grant when the
server rotates the refresh token.

New file-lock utility uses atomic file creation (O_EXCL) with stale
lock detection (30s timeout). New refreshAccessTokenSafe() wraps the
existing refresh with lock-acquire-then-recheck pattern: after acquiring
the lock, re-reads credentials in case another process already refreshed.

Falls back to unlocked refresh if lock acquisition fails (degraded but
functional). All callers (ensure-auth, background-refresh, credential-proxy)
now use the safe variant.
Only clear credentials on invalid_grant (refresh token permanently dead).
Network and server errors are transient — preserve credentials so the
user can retry without being forced to re-login.

Previously, any refresh failure wiped the keychain, destroying a
potentially valid refresh token over a temporary server hiccup.
- ensure-auth: collapse hasCredentials() + getCredentials() two-step
  into single getCredentials() call, eliminating double keyring read
  and fixing semantic mismatch (hasCredentials returns true for corrupt
  files, getCredentials returns null)

- credential-store/config-store: add migrationAttempted flag to prevent
  repeated failed keyring writes on every read when keyring is
  unavailable (e.g., headless Linux with no keyring daemon)
The "(Session refreshed)" message is implementation detail noise for
users. Moved to debug log, keeping only "Already logged in as ..."
in user-facing output.
@nicknisi nicknisi merged commit ac7922d into main Mar 5, 2026
5 checks passed
@nicknisi nicknisi deleted the nicknisi/auth-fixes branch March 5, 2026 20:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

1 participant