Skip to content

feat(cli): Pick the stored account from the target org or server - #1528

Merged
BYK merged 17 commits into
mainfrom
feat/cli-account-routing
Oct 19, 2026
Merged

BYK merged 17 commits into
mainfrom
feat/cli-account-routing

Conversation

@sentry-junior

@sentry-junior sentry-junior Bot commented Oct 16, 2026

Copy link
Copy Markdown
Contributor

Stacked on #1526. Covers the automatic-selection half of #1396: with several stored accounts, commands use the account that fits what they target, so users don't have to sentry auth switch back and forth.

How the account is chosen (per invocation, most specific first)

  1. Organization. If the target org isn't in the active account's cached org list, but another stored account has used that org, that account is pinned for the rest of the invocation. This hooks into resolveOrgRegion / resolveEffectiveOrg, so it covers the ~55 org-scoped call sites and DSN oNNNN ids.
  2. Server. An explicit SENTRY_HOST/SENTRY_URL, .sentryclirc, or a pasted Sentry URL for another account's server selects the stored account on that server. Previously this was a HostScopeError.
  3. Active account otherwise.

Guardrails

  • The stored active account never changes. The selection lives in memory, keyed by the invocation's env object, so library-mode calls are isolated from each other.
  • One invocation never mixes identities: the first pin wins.
  • An account is only chosen for hosts its token is already trusted for, using the same SaaS-equivalence rule as token-host.ts.
  • If the active account has no cached org list (for example right after auth switch), it is kept. An account that simply hasn't used the org yet can't be overridden by one that has.
  • An explicit server limits candidates to accounts on that server. The persisted defaults.url counts as soft: when the pinned account is elsewhere, it yields to that account's host.
  • SENTRY_FORCE_ENV_TOKEN is never overridden.

Storage
Schema v19 adds org_regions.account_id. Writes record the account whose credentials fetched the row, following in-process refresh-token rotation. The migration backfills existing rows by matching credential identity. The org→account mapping is warm right away for new logins, because login already warms the org cache.

Memoized credential state (token, fingerprint, row, stored-creds flag) is now keyed by selection, and user info reads and writes go to the account in effect. Account management (switch, logout, list) still works on the stored active account.

Not covered

  • An org that no stored account has used yet still goes to the active account. There is no retry-on-403 across accounts.
  • When several non-active accounts know the same org, the most recently used one wins, with no prompt.

via byk.

--

View Junior Session [Sentry]

@vercel

vercel Bot commented Oct 16, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated
sentry-local Skipped Skipped Oct 19, 2026 4:21pm UTC

Request Review

@sentry-junior
sentry-junior Bot added this pull request to stack #1529 October 16, 2026 20:39
@sentry-junior
sentry-junior Bot force-pushed the feat/cli-account-routing branch from b9d3f2c to a9699d8 Compare October 16, 2026 21:06
Comment thread packages/cli/src/lib/db/accounts.ts
Comment thread packages/cli/src/lib/db/accounts.ts Outdated
Comment thread packages/cli/src/lib/db/account-scope.ts
Comment thread packages/cli/src/lib/db/auth.ts
Comment thread packages/cli/src/lib/db/auth.ts Outdated
@sentry-junior
sentry-junior Bot force-pushed the feat/cli-account-routing branch from ad24de9 to 2139d1a Compare October 16, 2026 23:21
@sentry-junior
sentry-junior Bot force-pushed the feat/cli-account-routing branch from 0e933d8 to 46c6dc7 Compare October 17, 2026 00:21
@sentry-junior
sentry-junior Bot force-pushed the feat/cli-account-routing branch from 46c6dc7 to c288b7b Compare October 17, 2026 00:30
@sentry-junior
sentry-junior Bot force-pushed the feat/cli-account-routing branch from c288b7b to 66b331e Compare October 17, 2026 00:37
@BYK
BYK marked this pull request as ready for review October 17, 2026 01:09
@github-actions github-actions Bot added risk: medium PR risk score: medium and removed risk: medium PR risk score: medium labels Oct 17, 2026
@sentry-junior
sentry-junior Bot force-pushed the feat/cli-account-routing branch from 66b331e to dc83d07 Compare October 17, 2026 01:11
@github-actions github-actions Bot added the risk: medium PR risk score: medium label Oct 17, 2026

@cursor cursor Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale Bugbot comment from a previous run.

Comment thread packages/cli/src/lib/db/user.ts
Comment thread packages/cli/src/lib/db/auth.ts
…sts unsnapshotted

Replacing credentials in place (`sentry auth login --force`, automatic
re-login) wrote to the account selected for the configured server, so
SENTRY_HOST/SENTRY_URL or `.sentryclirc` naming another stored account's
server overwrote that account instead of the active one. The legacy NULL
host backfill had the same mix-up. Both now use the stored active account.

Pasting a SaaS URL read the active token host after clearing the URL; with
an env token in effect that snapshotted the token's host as SaaS even when
it was scoped to a self-hosted server. Compare the stored account in
effect instead, which never touches the env-token snapshot.
@BYK

BYK commented Oct 17, 2026

Copy link
Copy Markdown
Member

I wonder if we should modify commands like "org list" to list all available organizations under all accounts. Wdyt?

`sentry auth login --force` (and automatic re-login) replaced the stored
active account even when the login targeted another server, so a
self-hosted login could overwrite a SaaS account and vice versa.

Accounts are now always tied to their server: an in-place login replaces
the active account only when it is on the login's server, otherwise the
most recently used account on that server, and adds a new account when
that server has none. Accounts on other servers are never touched. The
`--force --token` path removes only that server's previous login, after
the new token validates.

Co-Authored-By: Burak Yigit Kaya <burak.kaya@sentry.io>

@BYK BYK left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like we can DRY this code up a bit. Also there are 2 important warden warnings we should address.

Comment thread packages/cli/src/lib/db/auth.ts Outdated
- Bake the org-list TTL into `hasCachedOrgList` (BYK review).
- DRY up server matching in db/auth: `getExplicitServerUrl()` and
  `findUsableAccountOnServer()` replace repeated inline logic, and every
  same-server check goes through `isAccountOnServer`.
- A pasted SaaS URL now always pins SENTRY_URL to SaaS when a SaaS
  account is stored, so org-based selection can't switch to a
  self-hosted account sharing the org slug or id (Warden).
- The command auth guard defers to target-based selection when the
  account in effect is logged out but another stored account can
  authenticate; requests still fail with AuthError when none is picked
  (Warden).

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 3600df9. Configure here.

Comment thread packages/cli/src/lib/sentry-url-parser.ts
The pin only applied when a usable SaaS account existed. With the SaaS
login logged out (now allowed past the auth guard) or no SaaS account at
all, org-based selection could still switch a pasted sentry.io URL to a
self-hosted account that shares the org slug. Pin SENTRY_URL to SaaS
whenever any account is stored, so only SaaS accounts are candidates and
an unusable one fails to authenticate instead.

@BYK BYK left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚀 Let's go

Pinning SENTRY_URL to SaaS whenever any account was stored broke the
single-account case: a lone login on another server (the e2e mock
server) pasting a sentry.io URL got a host-scope error instead of its
own server answering, as before multi-account. Pin only when a SaaS
account is stored (logged in or not) or accounts span several logins —
the cases where org-based selection could route the URL elsewhere.
…s SaaS

Pinning SENTRY_URL to SaaS for a pasted sentry.io URL left the persisted
default marker in place when defaults.url was already SaaS, so the pin
still read as the yielding default and org selection could switch to a
self-hosted account. Clear the marker when pinning.

Co-Authored-By: byk <burak.kaya@sentry.io>
Comment thread packages/cli/src/lib/command.ts
Comment thread packages/cli/src/lib/db/auth.ts Outdated
…g's account

- assertRcUrlTrusted accepts a non-SaaS .sentryclirc URL when a usable
  stored account is on it, like a pasted URL, so a logged-out active
  account no longer blocks org-based selection of a peer on that server.
- 401/403 scope recovery (and ensureScopesForAction) re-authorizes the
  account the invocation picked for its target org instead of replacing
  the active account, so the retry uses the new grant. The login must be
  approved as that account's user; otherwise nothing is stored.
Comment thread packages/cli/src/lib/region.ts
DSN resolution can yield an org id without its `o` prefix. Sentry org
slugs are never entirely numeric, so pass such ids to selectAccountForOrg
as the org id too; otherwise the account that has the org cached by id
was never selected.
The no-cached-org-list deference in selectAccountForOrg exists to respect
the user's active account after `auth switch`. An account chosen only
because it is the most recently used one on the configured server now
yields to a stored account on that server that knows the org.

Co-Authored-By: byk <burak.kaya@sentry.io>
@BYK
BYK merged commit 0ff95c2 into main Oct 19, 2026
26 checks passed
@BYK
BYK deleted the feat/cli-account-routing branch October 19, 2026 17:24

This branch was previously deployed

1 inactive deployment
Preview — 0c233dae Deployed Oct 19, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

risk: medium PR risk score: medium

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant