Skip to main content

ruma_client_api/rtc/livekit/
get_token.rs

1//! `POST /_matrix/client/*/rtc/livekit/get_token`
2//!
3//! Get a token to authenticate with a LiveKit SFU for a MatrixRTC slot.
4
5pub mod v1 {
6    //! `/v1/` ([MSC])
7    //!
8    //! [MSC]: https://github.com/matrix-org/matrix-spec-proposals/pull/4195
9
10    use ruma_common::{
11        OwnedRoomId, OwnedServerName,
12        api::{auth_scheme::AccessToken, request, response},
13        metadata,
14    };
15
16    metadata! {
17        method: POST,
18        rate_limited: true,
19        authentication: AccessToken,
20        history: {
21            unstable => "/_matrix/client/unstable/io.element.msc4195/rtc/livekit/get_token",
22        }
23    }
24
25    /// Request type for the `get_token` endpoint.
26    #[request]
27    pub struct Request {
28        /// The server name of the sender of the `m.rtc.member` event.
29        ///
30        /// If this is `None`, the homeserver uses its own server name.
31        #[serde(skip_serializing_if = "Option::is_none")]
32        pub server_name: Option<OwnedServerName>,
33
34        /// The WebSocket URL of the LiveKit SFU.
35        pub url: String,
36
37        /// The room where the `m.rtc.member` event is present.
38        pub room_id: OwnedRoomId,
39
40        /// The slot ID from the `m.rtc.member` event.
41        pub slot_id: String,
42
43        /// The `member.id` property of the requesting user's own `m.rtc.member` event.
44        pub member_id: String,
45    }
46
47    impl Request {
48        /// Creates a new `Request` with the given SFU URL, room ID, slot ID and member ID.
49        ///
50        /// The `server_name` field is set to `None`, which means that the homeserver uses its own
51        /// server name.
52        pub fn new(url: String, room_id: OwnedRoomId, slot_id: String, member_id: String) -> Self {
53            Self { server_name: None, url, room_id, slot_id, member_id }
54        }
55    }
56
57    /// Response type for the `get_token` endpoint.
58    #[response]
59    pub struct Response {
60        /// The JWT token to use for authentication with the SFU.
61        pub jwt: String,
62    }
63
64    impl Response {
65        /// Creates a new `Response` with the given JWT token. The corresponding SFU url is already
66        /// known via the request params.
67        pub fn new(jwt: String) -> Self {
68            Self { jwt }
69        }
70    }
71
72    #[cfg(all(test, feature = "client"))]
73    mod tests {
74        use std::borrow::Cow;
75
76        use ruma_common::{
77            api::{OutgoingRequestExt as _, SupportedVersions, auth_scheme::SendAccessToken},
78            owned_room_id, owned_server_name,
79        };
80        use serde_json::{Value as JsonValue, json};
81
82        use super::Request;
83
84        fn serialize_request(request: Request) -> (http::request::Parts, JsonValue) {
85            let supported =
86                SupportedVersions { versions: Default::default(), features: Default::default() };
87
88            let (parts, body) = request
89                .try_into_http_request::<Vec<u8>>(
90                    "https://homeserver.tld",
91                    SendAccessToken::IfRequired("auth_tok"),
92                    Cow::Owned(supported),
93                )
94                .unwrap()
95                .into_parts();
96
97            (parts, serde_json::from_slice(&body).unwrap())
98        }
99
100        #[test]
101        fn serialize_request_without_server_name() {
102            let request = Request::new(
103                "wss://livekit.example.com".to_owned(),
104                owned_room_id!("!tDLCaLXijNtYcJZEey:example.com"),
105                "the_id".to_owned(),
106                "xyzABCDEF10123".to_owned(),
107            );
108
109            let (parts, body) = serialize_request(request);
110
111            assert_eq!("POST", parts.method.as_str());
112            assert_eq!(
113                "https://homeserver.tld/_matrix/client/unstable/io.element.msc4195/rtc/livekit/get_token",
114                parts.uri.to_string()
115            );
116            assert_eq!(
117                body,
118                json!({
119                    "url": "wss://livekit.example.com",
120                    "room_id": "!tDLCaLXijNtYcJZEey:example.com",
121                    "slot_id": "the_id",
122                    "member_id": "xyzABCDEF10123",
123                })
124            );
125        }
126
127        #[test]
128        fn serialize_request_with_server_name() {
129            let mut request = Request::new(
130                "wss://livekit.example.com".to_owned(),
131                owned_room_id!("!tDLCaLXijNtYcJZEey:example.com"),
132                "the_id".to_owned(),
133                "xyzABCDEF10123".to_owned(),
134            );
135            request.server_name = Some(owned_server_name!("example.com"));
136
137            let (_parts, body) = serialize_request(request);
138
139            assert_eq!(
140                body,
141                json!({
142                    "server_name": "example.com",
143                    "url": "wss://livekit.example.com",
144                    "room_id": "!tDLCaLXijNtYcJZEey:example.com",
145                    "slot_id": "the_id",
146                    "member_id": "xyzABCDEF10123",
147                })
148            );
149        }
150    }
151
152    #[cfg(all(test, feature = "server"))]
153    mod server_tests {
154        use ruma_common::api::IncomingRequestExt as _;
155
156        use super::Request;
157
158        #[test]
159        fn deserialize_request() {
160            let request = http::Request::builder()
161                .method("POST")
162                .uri("/_matrix/client/unstable/io.element.msc4195/rtc/livekit/get_token")
163                .body(
164                    br#"{
165                        "url": "wss://livekit.example.org",
166                        "room_id": "!tDLCaLXijNtYcJZEey:example.org",
167                        "slot_id": "the_id",
168                        "member_id": "xyzABCDEF10123"
169                    }"# as &[u8],
170                )
171                .unwrap();
172
173            let request = Request::try_from_http_request(request, &[] as &[&str]).unwrap();
174
175            assert_eq!(request.server_name, None);
176            assert_eq!(request.url, "wss://livekit.example.org");
177            assert_eq!(request.room_id, "!tDLCaLXijNtYcJZEey:example.org");
178            assert_eq!(request.slot_id, "the_id");
179            assert_eq!(request.member_id, "xyzABCDEF10123");
180        }
181    }
182}