Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 12 additions & 3 deletions src/auth/callback.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ pub struct CallbackResult {
pub dd_oid: Option<String>,
/// Display name of the consented org (`dd_org_name`).
pub dd_org_name: Option<String>,
/// Actual Datadog site/region from the OAuth callback (`domain`).
pub domain: Option<String>,
}

#[cfg(not(target_arch = "wasm32"))]
Expand Down Expand Up @@ -199,6 +201,7 @@ async fn accept_loop(
error_description,
dd_oid,
dd_org_name,
domain,
};
if let Some(tx) = result_tx.lock().unwrap().take() {
let _ = tx.send(result);
Expand Down Expand Up @@ -294,6 +297,7 @@ fn parse_callback_url(input: &str) -> Result<CallbackResult> {
let mut error_description = None;
let mut dd_oid = None;
let mut dd_org_name = None;
let mut domain = None;
for (k, v) in url.query_pairs() {
match k.as_ref() {
"code" => code = Some(v.into_owned()),
Expand All @@ -302,6 +306,7 @@ fn parse_callback_url(input: &str) -> Result<CallbackResult> {
"error_description" => error_description = Some(v.into_owned()),
"dd_oid" => dd_oid = Some(v.into_owned()),
"dd_org_name" => dd_org_name = Some(v.into_owned()),
"domain" => domain = Some(v.into_owned()),
_ => {}
}
}
Expand All @@ -315,6 +320,7 @@ fn parse_callback_url(input: &str) -> Result<CallbackResult> {
error_description,
dd_oid,
dd_org_name,
domain,
})
}

Expand Down Expand Up @@ -366,24 +372,27 @@ mod tests {
assert!(r.error_description.is_none());
assert!(r.dd_oid.is_none());
assert!(r.dd_org_name.is_none());
assert!(r.domain.is_none());
}

#[test]
fn parse_callback_url_extracts_dd_oid_and_org_name() {
fn parse_callback_url_extracts_dd_oid_org_name_and_domain() {
// Real callback shape from a Datadog OAuth flow — the issuer appends
// dd_oid and (URL-encoded) dd_org_name alongside code and state.
// dd_oid, (URL-encoded) dd_org_name, and domain alongside code and state.
let r = parse_callback_url(
"http://127.0.0.1:8000/oauth/callback\
?code=abc&state=xyz\
&dd_oid=00000000-1111-2222-3333-444444444444\
&dd_org_name=Datadog+HQ",
&dd_org_name=Datadog+HQ\
&domain=us3.datadoghq.com",
)
.unwrap();
assert_eq!(
r.dd_oid.as_deref(),
Some("00000000-1111-2222-3333-444444444444")
);
assert_eq!(r.dd_org_name.as_deref(), Some("Datadog HQ"));
assert_eq!(r.domain.as_deref(), Some("us3.datadoghq.com"));
}

#[test]
Expand Down
11 changes: 7 additions & 4 deletions src/commands/auth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -171,9 +171,12 @@ pub async fn login(
bail!("OAuth state mismatch (possible CSRF attack)");
}

// 7. Exchange code for tokens
// 7. Exchange code for tokens.
// Use the actual site from the callback (e.g. "us3.datadoghq.com") when
// available, so the token exchange targets the correct region.
let effective_site = result.domain.as_deref().unwrap_or(site);
eprintln!("🔄 Exchanging authorization code for tokens...");
let tokens = dcr_client
let tokens = dcr::DcrClient::new(effective_site)
.exchange_code(&result.code, &redirect_uri, &challenge.verifier, &creds)
.await?;

Expand Down Expand Up @@ -221,7 +224,7 @@ pub async fn login(
let saved_org_label = org_suffix(saved_org);

let location = with_storage(|store| {
store.save_tokens(site, saved_org, &tokens)?;
store.save_tokens(effective_site, saved_org, &tokens)?;
Ok(store.storage_location())
})?;

Expand All @@ -235,7 +238,7 @@ pub async fn login(
.map(String::from)
.or_else(|| org_uuid.map(String::from));
storage::save_session(&storage::SessionEntry {
site: site.clone(),
site: effective_site.to_string(),
org: saved_org.map(String::from),
org_uuid: saved_org_uuid,
})?;
Expand Down
Loading