Skip to content

Commit b0574c1

Browse files
zecakehpoljar
authored andcommitted
feat(sdk): Add support for the stable m.oauth UIAA type
By replacing the custom implementation with the one in Ruma. Signed-off-by: Kévin Commaille <zecakeh@tedomum.fr>
1 parent bfd8cfa commit b0574c1

5 files changed

Lines changed: 103 additions & 51 deletions

File tree

Cargo.lock

Lines changed: 9 additions & 18 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ rand = "0.8.5"
6666
regex = "1.12.2"
6767
reqwest = { version = "0.12.24", default-features = false }
6868
rmp-serde = "1.3.0"
69-
ruma = { version = "0.14.0", features = [
69+
ruma = { git = "https://github.com/ruma/ruma", rev = "a67081e402dce14365089b34f50489dacc9c53b5", features = [
7070
"client-api-c",
7171
"compat-upload-signatures",
7272
"compat-arbitrary-length-ids",

crates/matrix-sdk/src/encryption/mod.rs

Lines changed: 7 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ use ruma::{
6262
to_device::send_event_to_device::v3::{
6363
Request as RumaToDeviceRequest, Response as ToDeviceResponse,
6464
},
65-
uiaa::{AuthData, UiaaInfo},
65+
uiaa::{AuthData, AuthType, OAuthParams, UiaaInfo},
6666
},
6767
assign,
6868
events::room::{MediaSource, ThumbnailInfo},
@@ -337,7 +337,7 @@ pub enum CrossSigningResetAuthType {
337337
impl CrossSigningResetAuthType {
338338
fn new(error: &HttpError) -> Result<Option<Self>> {
339339
if let Some(auth_info) = error.as_uiaa_response() {
340-
if let Ok(auth_info) = OAuthCrossSigningResetInfo::from_auth_info(auth_info) {
340+
if let Ok(Some(auth_info)) = OAuthCrossSigningResetInfo::from_auth_info(auth_info) {
341341
Ok(Some(CrossSigningResetAuthType::OAuth(auth_info)))
342342
} else {
343343
Ok(Some(CrossSigningResetAuthType::Uiaa(auth_info.clone())))
@@ -357,32 +357,15 @@ pub struct OAuthCrossSigningResetInfo {
357357
}
358358

359359
impl OAuthCrossSigningResetInfo {
360-
fn from_auth_info(auth_info: &UiaaInfo) -> Result<Self> {
361-
let parameters = serde_json::from_str::<OAuthCrossSigningResetUiaaParameters>(
362-
auth_info.params.as_ref().map(|value| value.get()).unwrap_or_default(),
363-
)?;
360+
fn from_auth_info(auth_info: &UiaaInfo) -> Result<Option<Self>> {
361+
let Some(parameters) = auth_info.params::<OAuthParams>(&AuthType::OAuth)? else {
362+
return Ok(None);
363+
};
364364

365-
Ok(OAuthCrossSigningResetInfo { approval_url: parameters.reset.url })
365+
Ok(Some(OAuthCrossSigningResetInfo { approval_url: parameters.url.as_str().try_into()? }))
366366
}
367367
}
368368

369-
/// The parsed `parameters` part of a [`ruma::api::client::uiaa::UiaaInfo`]
370-
/// response
371-
#[derive(Debug, Deserialize)]
372-
struct OAuthCrossSigningResetUiaaParameters {
373-
/// The URL where the user can approve the reset of the cross-signing keys.
374-
#[serde(rename = "org.matrix.cross_signing_reset")]
375-
reset: OAuthCrossSigningResetUiaaResetParameter,
376-
}
377-
378-
/// The `org.matrix.cross_signing_reset` part of the Uiaa response `parameters``
379-
/// dictionary.
380-
#[derive(Debug, Deserialize)]
381-
struct OAuthCrossSigningResetUiaaResetParameter {
382-
/// The URL where the user can approve the reset of the cross-signing keys.
383-
url: Url,
384-
}
385-
386369
/// A struct that helps to parse the custom error message Synapse posts if a
387370
/// duplicate one-time key is uploaded.
388371
#[derive(Debug)]

crates/matrix-sdk/src/test_utils/mocks/mod.rs

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3629,8 +3629,8 @@ impl<'a> MockEndpoint<'a, UploadCrossSigningKeysEndpoint> {
36293629
})))
36303630
}
36313631

3632-
/// Returns an error response with an OAuth 2.0 UIAA stage.
3633-
pub fn uiaa_oauth(self) -> MatrixMock<'a> {
3632+
/// Returns an error response with an unstable OAuth 2.0 UIAA stage.
3633+
pub fn uiaa_unstable_oauth(self) -> MatrixMock<'a> {
36343634
let server_uri = self.server.uri();
36353635
self.respond_with(ResponseTemplate::new(401).set_body_json(json!({
36363636
"session": "dummy",
@@ -3645,6 +3645,23 @@ impl<'a> MockEndpoint<'a, UploadCrossSigningKeysEndpoint> {
36453645
"msg": "To reset your end-to-end encryption cross-signing identity, you first need to approve it and then try again."
36463646
})))
36473647
}
3648+
3649+
/// Returns an error response with a stable OAuth 2.0 UIAA stage.
3650+
pub fn uiaa_stable_oauth(self) -> MatrixMock<'a> {
3651+
let server_uri = self.server.uri();
3652+
self.respond_with(ResponseTemplate::new(401).set_body_json(json!({
3653+
"session": "dummy",
3654+
"flows": [{
3655+
"stages": [ "m.oauth" ]
3656+
}],
3657+
"params": {
3658+
"m.oauth": {
3659+
"url": format!("{server_uri}/account/?action=org.matrix.cross_signing_reset"),
3660+
}
3661+
},
3662+
"msg": "To reset your end-to-end encryption cross-signing identity, you first need to approve it and then try again."
3663+
})))
3664+
}
36483665
}
36493666

36503667
/// A prebuilt mock for `POST /keys/signatures/upload` request.

crates/matrix-sdk/tests/integration/encryption/cross_signing.rs

Lines changed: 67 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ use assert_matches2::assert_let;
1616
use matrix_sdk::{encryption::CrossSigningResetAuthType, test_utils::mocks::MatrixMockServer};
1717
use matrix_sdk_test::async_test;
1818
use ruma::api::client::uiaa;
19+
use similar_asserts::assert_eq;
1920

2021
#[async_test]
2122
async fn test_reset_legacy_auth() {
@@ -98,25 +99,85 @@ async fn test_reset_legacy_auth_invalid_password() {
9899
}
99100

100101
#[async_test]
101-
async fn test_reset_oauth() {
102-
use assert_matches2::assert_let;
103-
use matrix_sdk::{encryption::CrossSigningResetAuthType, test_utils::mocks::MatrixMockServer};
104-
use similar_asserts::assert_eq;
102+
async fn test_reset_unstable_oauth() {
103+
let server = MatrixMockServer::new().await;
104+
let client = server.client_builder().logged_in_with_oauth().build().await;
105+
106+
assert!(
107+
!client.encryption().cross_signing_status().await.unwrap().is_complete(),
108+
"Initially we shouldn't have any cross-signing keys",
109+
);
110+
111+
server.mock_upload_keys().ok().expect(1).named("Initial device keys upload").mount().await;
105112

113+
// Return the UIAA response 5 times.
114+
server
115+
.mock_upload_cross_signing_keys()
116+
.uiaa_unstable_oauth()
117+
.up_to_n_times(5)
118+
.expect(5)
119+
.named("Trying to upload the cross-signing keys with UIAA response")
120+
.mount()
121+
.await;
122+
123+
// And finally succeed.
124+
// This works because the first mocked endpoint that matches the path is used
125+
// until it is invalidated by `up_to_n_times`.
126+
server
127+
.mock_upload_cross_signing_keys()
128+
.ok()
129+
.expect(1)
130+
.named("Succeeding to upload the cross-signing keys")
131+
.mount()
132+
.await;
133+
134+
server
135+
.mock_upload_cross_signing_signatures()
136+
.ok()
137+
.expect(1)
138+
.named("Final signatures upload")
139+
.mount()
140+
.await;
141+
142+
// First requests gives us a reset handle.
143+
let reset_handle = client
144+
.encryption()
145+
.reset_cross_signing()
146+
.await
147+
.unwrap()
148+
.expect("We should have received a reset handle");
149+
150+
assert_let!(CrossSigningResetAuthType::OAuth(oauth_info) = reset_handle.auth_type());
151+
assert_eq!(
152+
oauth_info.approval_url.as_str(),
153+
format!("{}/account/?action=org.matrix.cross_signing_reset", server.uri())
154+
);
155+
156+
// Then it retries until it succeeds.
157+
reset_handle.auth(None).await.expect("We should be able to reset the cross-signing keys after some attempts, waiting for the auth issue to allow us to upload");
158+
159+
assert!(
160+
client.encryption().cross_signing_status().await.unwrap().is_complete(),
161+
"After the reset we have the cross-signing available.",
162+
);
163+
}
164+
165+
#[async_test]
166+
async fn test_reset_stable_oauth() {
106167
let server = MatrixMockServer::new().await;
107168
let client = server.client_builder().logged_in_with_oauth().build().await;
108169

109170
assert!(
110171
!client.encryption().cross_signing_status().await.unwrap().is_complete(),
111-
"Initially we shouldn't have any cross-signin keys",
172+
"Initially we shouldn't have any cross-signing keys",
112173
);
113174

114175
server.mock_upload_keys().ok().expect(1).named("Initial device keys upload").mount().await;
115176

116177
// Return the UIAA response 5 times.
117178
server
118179
.mock_upload_cross_signing_keys()
119-
.uiaa_oauth()
180+
.uiaa_stable_oauth()
120181
.up_to_n_times(5)
121182
.expect(5)
122183
.named("Trying to upload the cross-signing keys with UIAA response")

0 commit comments

Comments
 (0)