fix(auth): Route org token requests to region_url#3342
Conversation
Org auth tokens for multi-region SaaS orgs (e.g. us2, s4s2) embed a control-silo url (https://sentry.io) plus a region_url (https://<region>.sentry.io) that serves the org's API requests. The CLI only parsed url and discarded region_url, so region-siloed requests such as snapshot uploads were sent to the control silo and rejected with 401 Invalid org token. Parse region_url from the token payload and prefer it as the base URL, falling back to url when absent. This routes all commands to the correct region automatically, with no need for a manual --url flag. Tokens where url equals region_url (self-hosted, classic SaaS) are unaffected. A token with an empty url but a present region_url now routes to region_url and overrides --url, where it previously let --url take precedence.
Adds the changelog entry required by Danger CI for #3342.
| // Region URL is the host that actually serves this org's API requests. It is | ||
| // absent from older tokens, in which case requests fall back to `url`. | ||
| #[serde(default, deserialize_with = "url_deserializer")] | ||
| pub region_url: String, |
There was a problem hiding this comment.
| pub region_url: String, | |
| pub region_url: Option<String>, |
This would be the proper type, instead of using an empty string as a sentinel value
There was a problem hiding this comment.
And I guess the reason the clanker used String is that we're gonna need a new opt_url_deserializer to do it that way
|
@szokeasaurusrex this change enabled me to locally upload snapshots to us2 but im not 100% confident as i'm not working on the us2 project directly so ive shared this PR with that team and won't merge until they signoff too |
| if self.region_url.is_empty() { | ||
| &self.url | ||
| } else { | ||
| &self.region_url | ||
| } |
There was a problem hiding this comment.
This part makes sense to me as most CLI operations (and org auth tokens in general) work on regional resources. This would make it so that org-auth-tokens can't be used on anything that involves control though.
There was a problem hiding this comment.
+1 on this: Broadly switching all operations to prefer the region URL could be risky; we should validate that all endpoints supported by org auth tokens also are available on the region.
Less risky could be to expose a way to get the region URL while still maintaining a way to get the base URL, although that is likely a bit complex. That would allow us to use the region URL where needed and for commands where we have validated that it works, while continuing to use the base URL elsewhere.
There was a problem hiding this comment.
I noticed that an old version of CLI had this utility
Line 1191 in de5cda2
Maybe relevant for the discussion here?
There was a problem hiding this comment.
Yeah, we had used this for the pre-chunked-uploading uploads that were removed in Sentry CLI 3.x. We no longer needed it for chunked uploads, as the chunked upload endpoint already returns the region-specific URL as the upload URL, which is why this was removed.
If we go with a fix in the CLI, though, we might want something like this utility so that we only migrate the API calls that need to go to the region-specific endpoints over to the region specific URLs; that lowers the risk of this change by keeping most things on control silo.
|
We can avoid doing this by changing the nginx routing logic fortunately. i didn't fully deploy the changes yesterday and something broke |
Summary
Org auth tokens (
sntrys_) for multi-region SaaS orgs carry two hosts in their payload:url— the control silo (https://sentry.io)region_url— where the org's API requests are actually served (https://<region>.sentry.io)The CLI only parsed
urland discardedregion_urlentirely. For region-scoped org tokens (e.g.us2,s4s2), that meant region-siloed requests — likesnapshots upload— were sent to the control silo and rejected with401 Invalid org token. Decoded examples (both real region tokens):{ "url": "https://sentry.io", "region_url": "https://us2.sentry.io", "org": "us2-test-org-sc" } { "url": "https://sentry.io", "region_url": "https://s4s2.sentry.io", "org": "sentry-s4s2" }Passing
--urldid not help: when a token embeds a URL, the CLI overrides--urlwith the token's URL — so requests still went tosentry.io.Fix
region_urlfrom the token payload (AuthTokenPayload).base_url()helper that prefersregion_url, falling back tourlwhen absent.base_url()everywhereconfig.rsderives the API host (from_file,set_auth,set_base_url).Result: every command routes to the correct region automatically, with no
--urlflag needed. Verified end-to-end — asnapshots uploadtosentry-s4s2now succeeds (previously401).Compatibility / behavior notes
url == region_url(self-hosted, classic SaaS) and legacy tokens with noregion_urlare unaffected —base_url()returns the same value as before.urlbut a presentregion_urlnow routes toregion_urland overrides--url, whereas it previously let--urltake precedence. This is the correct behavior for region-scoped tokens and keeps the no-flag default routing to the right region.--url.Tests
base_url()precedence: differingurl/region_url, nullurlwith presentregion_url, and a legacy token withregion_urlabsent.trycmd): newregion-url-routingcase assertingregion_urlis preferred over both the token'surland--url; updatedurl-mismatch-empty-tokento reflect the new behavior.org_tokensintegration cases pass;clippyclean;cargo fmtapplied.