Skip to content
This repository was archived by the owner on Sep 10, 2024. It is now read-only.

Commit 83bf739

Browse files
committed
Implement account management discovery as per MSC2965
1 parent b5fb65b commit 83bf739

File tree

5 files changed

+99
-2
lines changed

5 files changed

+99
-2
lines changed

crates/handlers/src/oauth2/discovery.rs

+13
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,10 @@ struct DiscoveryResponse {
3434

3535
#[serde(rename = "org.matrix.matrix-authentication-service.graphql_endpoint")]
3636
graphql_endpoint: url::Url,
37+
38+
// As per MSC2965
39+
account_management_uri: url::Url,
40+
account_management_actions_supported: Vec<String>,
3741
}
3842

3943
#[tracing::instrument(name = "handlers.oauth2.discovery.get", skip_all)]
@@ -168,6 +172,15 @@ pub(crate) async fn get(
168172
Json(DiscoveryResponse {
169173
standard,
170174
graphql_endpoint: url_builder.graphql_endpoint(),
175+
account_management_uri: url_builder.account_management_uri(),
176+
// This needs to be kept in sync with what is supported in the frontend,
177+
// see frontend/src/routing/actions.ts
178+
account_management_actions_supported: vec![
179+
"org.matrix.profile".to_owned(),
180+
"org.matrix.sessions_list".to_owned(),
181+
"org.matrix.session_view".to_owned(),
182+
"org.matrix.session_end".to_owned(),
183+
],
171184
})
172185
}
173186

crates/router/src/endpoints.rs

+16-1
Original file line numberDiff line numberDiff line change
@@ -429,11 +429,26 @@ impl AccountAddEmail {
429429

430430
/// Actions parameters as defined by MSC2965
431431
#[derive(Debug, Clone, Serialize, Deserialize)]
432-
#[serde(rename_all = "snake_case", tag = "action")]
432+
#[serde(tag = "action")]
433433
pub enum AccountAction {
434+
#[serde(rename = "org.matrix.profile")]
435+
OrgMatrixProfile,
436+
#[serde(rename = "profile")]
434437
Profile,
438+
439+
#[serde(rename = "org.matrix.sessions_list")]
440+
OrgMatrixSessionsList,
441+
#[serde(rename = "sessions_list")]
435442
SessionsList,
443+
444+
#[serde(rename = "org.matrix.session_view")]
445+
OrgMatrixSessionView { device_id: String },
446+
#[serde(rename = "session_view")]
436447
SessionView { device_id: String },
448+
449+
#[serde(rename = "org.matrix.session_end")]
450+
OrgMatrixSessionEnd { device_id: String },
451+
#[serde(rename = "session_end")]
437452
SessionEnd { device_id: String },
438453
}
439454

crates/router/src/url_builder.rs

+6
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,12 @@ impl UrlBuilder {
195195
pub fn upstream_oauth_authorize(&self, id: Ulid) -> Url {
196196
self.absolute_url_for(&crate::endpoints::UpstreamOAuth2Authorize::new(id))
197197
}
198+
199+
/// Account management URI
200+
#[must_use]
201+
pub fn account_management_uri(&self) -> Url {
202+
self.absolute_url_for(&crate::endpoints::Account::default())
203+
}
198204
}
199205

200206
#[cfg(test)]

frontend/src/routing/actions.test.ts

+57
Original file line numberDiff line numberDiff line change
@@ -95,4 +95,61 @@ describe("getRouteActionRedirection()", () => {
9595
searchParams: new URLSearchParams(),
9696
});
9797
});
98+
99+
it("redirects to session detail when location has a action=org.matrix.session_end", () => {
100+
const searchParams = new URLSearchParams();
101+
searchParams.set("action", "org.matrix.session_end");
102+
searchParams.set("device_id", "test-device-id");
103+
searchParams.set("something_else", "should-remain");
104+
expect(
105+
getRouteActionRedirection({ pathname: "/account/", searchParams }),
106+
).toEqual({
107+
route: {
108+
type: "session",
109+
id: "test-device-id",
110+
},
111+
searchParams: new URLSearchParams("?something_else=should-remain"),
112+
});
113+
});
114+
115+
it("redirects to session detail when location has a action=org.matrix.session_view", () => {
116+
const searchParams = new URLSearchParams();
117+
searchParams.set("action", "org.matrix.session_view");
118+
searchParams.set("device_id", "test-device-id");
119+
expect(
120+
getRouteActionRedirection({ pathname: "/account/", searchParams }),
121+
).toEqual({
122+
route: {
123+
type: "session",
124+
id: "test-device-id",
125+
},
126+
searchParams: new URLSearchParams(),
127+
});
128+
});
129+
130+
it("redirects to sessions overview when location has a action=org.matrix.sessions_list", () => {
131+
const searchParams = new URLSearchParams();
132+
searchParams.set("action", "org.matrix.sessions_list");
133+
expect(
134+
getRouteActionRedirection({ pathname: "/account/", searchParams }),
135+
).toEqual({
136+
route: {
137+
type: "sessions-overview",
138+
},
139+
searchParams: new URLSearchParams(),
140+
});
141+
});
142+
143+
it("redirects to profile when location has a action=org.matrix.profile", () => {
144+
const searchParams = new URLSearchParams();
145+
searchParams.set("action", "org.matrix.profile");
146+
expect(
147+
getRouteActionRedirection({ pathname: "/account/", searchParams }),
148+
).toEqual({
149+
route: {
150+
type: "profile",
151+
},
152+
searchParams: new URLSearchParams(),
153+
});
154+
});
98155
});

frontend/src/routing/actions.ts

+7-1
Original file line numberDiff line numberDiff line change
@@ -32,11 +32,17 @@ export const getRouteActionRedirection = (
3232
} => {
3333
// Clone the search params so we can modify them
3434
const searchParams = new URLSearchParams(location.searchParams?.toString());
35-
const action = searchParams.get("action");
35+
let action = searchParams.get("action");
3636
const deviceId = searchParams.get("device_id");
3737
searchParams.delete("action");
3838
searchParams.delete("device_id");
3939

40+
// Actions are actually prefixed with org.matrix. in the latest version of MSC2965
41+
// but we still want to support non-prefixed actions for backwards compatibility
42+
if (action) {
43+
action = action.replace(/^org.matrix./, "");
44+
}
45+
4046
let route: Route;
4147
switch (action) {
4248
case RouteAction.EndSession:

0 commit comments

Comments
 (0)