feat(auth): hint when env token is shadowed by stored OAuth (#785)#790
feat(auth): hint when env token is shadowed by stored OAuth (#785)#790
Conversation
The most painful UX item from #785: a user sets SENTRY_AUTH_TOKEN (e.g. from a Stripe Projects or Vercel integration) but also has a stored OAuth login from `sentry auth login`. The CLI silently prefers the stored login — the user's 30 minutes of debugging invariably ends at discovering `SENTRY_FORCE_ENV_TOKEN=1`. Surface the collision on stderr the first time an authenticated command hits the API: [info] [auth] Detected SENTRY_AUTH_TOKEN env var but using stored login for alice. Set SENTRY_FORCE_ENV_TOKEN=1 to prefer the env var. Gating: - Fires only when an env token is set AND a stored OAuth login exists AND SENTRY_FORCE_ENV_TOKEN is not set. - Fires at most once per process (module-local latch). - Fires inside `authenticatedFetch` so local-only commands like `sentry help` or `sentry cli upgrade` stay quiet. User label resolution prefers `username` → `email` → `name` → "stored OAuth user" fallback, matching what `sentry auth whoami` shows when the cache is cold. Addresses #785 item #4.
Semver Impact of This PR🟡 Minor (new features) 📋 Changelog PreviewThis is how your changes will appear in the changelog. New Features ✨
Bug Fixes 🐛
Internal Changes 🔧
🤖 This preview updates automatically when you update the PR. |
|
Codecov Results 📊✅ 138 passed | Total: 138 | Pass Rate: 100% | Execution Time: 0ms 📊 Comparison with Base Branch
✨ No test changes detected All tests are passing successfully. ✅ Patch coverage is 93.02%. Project has 1698 uncovered lines. Files with missing lines (1)
Coverage diff@@ Coverage Diff @@
## main #PR +/-##
==========================================
+ Coverage 95.52% 95.58% +0.06%
==========================================
Files 262 264 +2
Lines 38174 38444 +270
Branches 0 0 —
==========================================
+ Hits 36464 36746 +282
- Misses 1710 1698 -12
- Partials 0 0 —Generated by Codecov Action |
| } catch { | ||
| // DB access failed — silently skip; this is a best-effort hint. | ||
| return; | ||
| } | ||
| if (!hasStored) { | ||
| return; | ||
| } | ||
|
|
||
| hintEmitted = true; |
There was a problem hiding this comment.
Should probably report the error? Also hasStored would still be false if an exception is thrown so the explicit return in the catch block is unnecessary.
There was a problem hiding this comment.
Good catches — both fixed in 83076f5:
Sentry.captureException(error)in both catch blocks (resolveStoredUserLabelhad the same pattern — fixed there too).- Removed the redundant
returnin thehasStoredAuthCredentialscatch;hasStoredstaysfalseso the existingif (!hasStored) returnguard handles it. Added a comment explaining the fall-through.
Address review comment on #790: the best-effort guards in maybeWarnEnvTokenIgnored() and resolveStoredUserLabel() silently swallowed DB read failures, making persistent cache-read problems invisible. Report them via Sentry.captureException while keeping the CLI-UX behavior unchanged (still falls back to silent-no-hint and neutral-user-label respectively). Also drops the redundant early `return` in the hasStoredAuthCredentials catch block — `hasStored` stays `false` after the throw, so the existing `if (!hasStored) return` guard handles it.
Summary
The most painful UX item from #785: a user sets
SENTRY_AUTH_TOKEN(e.g. from a Stripe Projects or Vercel integration) but also has a stored OAuth login fromsentry auth login. The CLI silently prefers the stored login — the user's 30 minutes of debugging invariably ends at discoveringSENTRY_FORCE_ENV_TOKEN=1.Surface the collision on stderr the first time an authenticated command hits the API:
Gating:
SENTRY_FORCE_ENV_TOKENis not set.authenticatedFetch, so local-only commands (sentry help,sentry cli upgrade) stay quiet.User label resolution prefers
username→email→name→"stored OAuth user"fallback, matching whatsentry auth whoamishows when the cache is cold.Test plan
test/lib/auth-hint.test.tscovers: fires on collision, silent without env token, silent without stored OAuth, silent underSENTRY_FORCE_ENV_TOKEN, once-per-process latch, user label fallbacks,SENTRY_TOKENlegacy var support.bun run typecheck,bun run lint— clean.Part of #785 (addresses item #4).