From e3865dbfa0de3022be6e1f73d1bc0afc67daf6da Mon Sep 17 00:00:00 2001 From: syui Date: Mon, 23 Mar 2026 06:31:55 +0900 Subject: [PATCH] fix bot dpop --- src/mcp/mod.rs | 56 +++++++++++++++++++++++++++++++++++++------------- 1 file changed, 42 insertions(+), 14 deletions(-) diff --git a/src/mcp/mod.rs b/src/mcp/mod.rs index 8c69954..8ef604b 100644 --- a/src/mcp/mod.rs +++ b/src/mcp/mod.rs @@ -406,27 +406,55 @@ fn handle_post_create(text: &str) -> Result { let url = format!("https://{}/xrpc/com.atproto.repo.putRecord", pds); let client = reqwest::blocking::Client::new(); - // Build request with appropriate auth (DPoP or Bearer) - let request = if oauth::has_oauth_session(true) { + // Build and send request with appropriate auth (DPoP with nonce retry, or Bearer) + let (status, resp_body) = if oauth::has_oauth_session(true) { let oauth_session = oauth::load_oauth_session(true)?; let full_url = format!("https://{}/xrpc/com.atproto.repo.putRecord", pds); - let dpop_proof = oauth::create_dpop_proof_for_request_with_nonce( - &oauth_session, "POST", &full_url, None, - )?; - client.post(&url) - .header("Authorization", format!("DPoP {}", bot_session.access_jwt)) - .header("DPoP", dpop_proof) - .json(&body) + let mut dpop_nonce: Option = None; + let mut last_status = reqwest::StatusCode::INTERNAL_SERVER_ERROR; + let mut last_body = String::new(); + + for _attempt in 0..3 { + let dpop_proof = oauth::create_dpop_proof_for_request_with_nonce( + &oauth_session, "POST", &full_url, dpop_nonce.as_deref(), + )?; + let res = client.post(&url) + .header("Authorization", format!("DPoP {}", bot_session.access_jwt)) + .header("DPoP", dpop_proof) + .json(&body) + .send() + .map_err(|e| anyhow::anyhow!("HTTP request failed: {}", e))?; + + last_status = res.status(); + + // Check for dpop-nonce requirement + if last_status == reqwest::StatusCode::UNAUTHORIZED { + let nonce = res.headers().get("dpop-nonce").and_then(|v| v.to_str().ok()).map(|s| s.to_string()); + last_body = res.text().unwrap_or_default(); + if let Some(n) = nonce { + if last_body.contains("use_dpop_nonce") { + dpop_nonce = Some(n); + continue; + } + } + break; + } + + last_body = res.text().unwrap_or_default(); + break; + } + (last_status, last_body) } else { - client.post(&url) + let res = client.post(&url) .header("Authorization", format!("Bearer {}", bot_session.access_jwt)) .json(&body) + .send() + .map_err(|e| anyhow::anyhow!("HTTP request failed: {}", e))?; + let status = res.status(); + let resp_body = res.text().unwrap_or_default(); + (status, resp_body) }; - let res = request.send().map_err(|e| anyhow::anyhow!("HTTP request failed: {}", e))?; - let status = res.status(); - let resp_body = res.text().unwrap_or_default(); - if !status.is_success() { return Err(anyhow::anyhow!("Post failed ({}): {}", status, resp_body)); }