From ec975094e2c12e83c85597bbae7692801ace3c5d Mon Sep 17 00:00:00 2001 From: Andy Balaam Date: Fri, 20 Mar 2026 10:13:44 +0000 Subject: [PATCH] Throttle our retries when attempting cross-signing reset, and give up after a time limit (#6325) Related to https://github.com/element-hq/element-x-android/pull/6420 Part of https://github.com/element-hq/element-x-android/issues/5075 I noticed when investigating a bug about resetting your identity when using a MAS login that we poll the server checking whether the user has given us permission with no limit on how fast we poll, and with no ability to give up if it's not working. This change causes us to retry only twice per second, and give up after 2 minutes. These are guesses as to the right values and I am open to discussion. --- crates/matrix-sdk/CHANGELOG.md | 3 ++ crates/matrix-sdk/src/encryption/mod.rs | 55 ++++++++++++++++++------- crates/matrix-sdk/src/error.rs | 4 ++ 3 files changed, 47 insertions(+), 15 deletions(-) diff --git a/crates/matrix-sdk/CHANGELOG.md b/crates/matrix-sdk/CHANGELOG.md index be5a51a8e..703be0e1f 100644 --- a/crates/matrix-sdk/CHANGELOG.md +++ b/crates/matrix-sdk/CHANGELOG.md @@ -8,6 +8,9 @@ All notable changes to this project will be documented in this file. ### Features +- [**breaking**] `matrix_sdk::error::Error` has a new variant `Timeout` which occurs when + a cross-signing reset does not succeed after some period of time. + ([#6325](https://github.com/matrix-org/matrix-rust-sdk/pull/6325)) - The `beacon_info` start event ([MSC3672](https://github.com/matrix-org/matrix-spec-proposals/pull/3672)) is now included when computing the latest event for a room, so live location sharing sessions can be surfaced as a room's most recent activity. diff --git a/crates/matrix-sdk/src/encryption/mod.rs b/crates/matrix-sdk/src/encryption/mod.rs index bfe143511..98cdce7a4 100644 --- a/crates/matrix-sdk/src/encryption/mod.rs +++ b/crates/matrix-sdk/src/encryption/mod.rs @@ -25,6 +25,7 @@ use std::{ path::PathBuf, str::FromStr, sync::Arc, + time::Duration, }; use eyeball::{SharedObservable, Subscriber}; @@ -48,6 +49,7 @@ use matrix_sdk_base::{ }, }, }, + sleep::sleep, }; use matrix_sdk_common::{executor::spawn, locks::Mutex as StdMutex}; use ruma::{ @@ -298,27 +300,50 @@ impl CrossSigningResetHandle { /// authentication to be done on the side of the OAuth 2.0 server or by /// providing additional [`AuthData`] the homeserver requires. pub async fn auth(&self, auth: Option) -> Result<()> { - let mut upload_request = self.upload_request.clone(); - upload_request.auth = auth; + // Poll to see whether the reset has been authorized twice per second. + const RETRY_EVERY: Duration = Duration::from_millis(500); - while let Err(e) = self.client.send(upload_request.clone()).await { - if *self.is_cancelled.lock().await { - return Ok(()); - } + // Give up after two minutes of polling. + const TIMEOUT: Duration = Duration::from_mins(2); - match e.as_uiaa_response() { - Some(uiaa_info) => { - if uiaa_info.auth_error.is_some() { - return Err(e.into()); - } + tokio::time::timeout(TIMEOUT, async { + let mut upload_request = self.upload_request.clone(); + upload_request.auth = auth; + + debug!( + "Repeatedly PUTting to keys/device_signing/upload until it works \ + or we hit a permanent failure." + ); + while let Err(e) = self.client.send(upload_request.clone()).await { + if *self.is_cancelled.lock().await { + return Ok(()); } - None => return Err(e.into()), + + match e.as_uiaa_response() { + Some(uiaa_info) => { + if uiaa_info.auth_error.is_some() { + return Err(e.into()); + } + } + None => return Err(e.into()), + } + + debug!( + "PUT to keys/device_signing/upload failed with 401. Retrying after \ + a short delay." + ); + sleep(RETRY_EVERY).await; } - } - self.client.send(self.signatures_request.clone()).await?; + self.client.send(self.signatures_request.clone()).await?; - Ok(()) + Ok(()) + }) + .await + .unwrap_or_else(|_| { + warn!("Timed out waiting for keys/device_signing/upload to succeed."); + Err(Error::Timeout) + }) } /// Cancel the ongoing identity reset process diff --git a/crates/matrix-sdk/src/error.rs b/crates/matrix-sdk/src/error.rs index 582380616..6ea097f50 100644 --- a/crates/matrix-sdk/src/error.rs +++ b/crates/matrix-sdk/src/error.rs @@ -417,6 +417,10 @@ pub enum Error { /// An error happened while attempting to change power levels. #[error("power levels error: {0}")] PowerLevels(#[from] PowerLevelsError), + + /// We timed out attempting to complete an operation. + #[error("timed out")] + Timeout, } #[rustfmt::skip] // stop rustfmt breaking the `` in docs across multiple lines