matrix-js-sdk 43.0.0 → 44.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (88) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/lib/matrixrtc/CallMembership.d.ts +30 -25
  3. package/lib/matrixrtc/CallMembership.d.ts.map +1 -1
  4. package/lib/matrixrtc/CallMembership.js +91 -100
  5. package/lib/matrixrtc/CallMembership.js.map +1 -1
  6. package/lib/matrixrtc/EncryptionManager.d.ts +6 -4
  7. package/lib/matrixrtc/EncryptionManager.d.ts.map +1 -1
  8. package/lib/matrixrtc/EncryptionManager.js.map +1 -1
  9. package/lib/matrixrtc/IMembershipManager.d.ts +2 -15
  10. package/lib/matrixrtc/IMembershipManager.d.ts.map +1 -1
  11. package/lib/matrixrtc/IMembershipManager.js.map +1 -1
  12. package/lib/matrixrtc/LivekitTransport.d.ts +36 -7
  13. package/lib/matrixrtc/LivekitTransport.d.ts.map +1 -1
  14. package/lib/matrixrtc/LivekitTransport.js +29 -2
  15. package/lib/matrixrtc/LivekitTransport.js.map +1 -1
  16. package/lib/matrixrtc/MatrixRTCSession.d.ts +12 -51
  17. package/lib/matrixrtc/MatrixRTCSession.d.ts.map +1 -1
  18. package/lib/matrixrtc/MatrixRTCSession.js +10 -66
  19. package/lib/matrixrtc/MatrixRTCSession.js.map +1 -1
  20. package/lib/matrixrtc/MembershipManager.d.ts +5 -10
  21. package/lib/matrixrtc/MembershipManager.d.ts.map +1 -1
  22. package/lib/matrixrtc/MembershipManager.js +25 -44
  23. package/lib/matrixrtc/MembershipManager.js.map +1 -1
  24. package/lib/matrixrtc/RTCEncryptionManager.d.ts +8 -8
  25. package/lib/matrixrtc/RTCEncryptionManager.d.ts.map +1 -1
  26. package/lib/matrixrtc/RTCEncryptionManager.js +30 -22
  27. package/lib/matrixrtc/RTCEncryptionManager.js.map +1 -1
  28. package/lib/matrixrtc/ToDeviceKeyTransport.js +7 -8
  29. package/lib/matrixrtc/ToDeviceKeyTransport.js.map +1 -1
  30. package/lib/matrixrtc/membershipData/rtc.d.ts +8 -13
  31. package/lib/matrixrtc/membershipData/rtc.d.ts.map +1 -1
  32. package/lib/matrixrtc/membershipData/rtc.js +2 -11
  33. package/lib/matrixrtc/membershipData/rtc.js.map +1 -1
  34. package/lib/matrixrtc/membershipData/session.d.ts +6 -2
  35. package/lib/matrixrtc/membershipData/session.d.ts.map +1 -1
  36. package/lib/matrixrtc/membershipData/session.js +3 -0
  37. package/lib/matrixrtc/membershipData/session.js.map +1 -1
  38. package/lib/matrixrtc/types.d.ts +5 -0
  39. package/lib/matrixrtc/types.d.ts.map +1 -1
  40. package/lib/matrixrtc/types.js +5 -0
  41. package/lib/matrixrtc/types.js.map +1 -1
  42. package/lib/models/event-timeline-set.d.ts +9 -2
  43. package/lib/models/event-timeline-set.d.ts.map +1 -1
  44. package/lib/models/event-timeline-set.js +8 -5
  45. package/lib/models/event-timeline-set.js.map +1 -1
  46. package/lib/models/room-sticky-events.d.ts +17 -2
  47. package/lib/models/room-sticky-events.d.ts.map +1 -1
  48. package/lib/models/room-sticky-events.js +88 -47
  49. package/lib/models/room-sticky-events.js.map +1 -1
  50. package/lib/models/room.d.ts +1 -1
  51. package/lib/models/room.d.ts.map +1 -1
  52. package/lib/models/room.js +8 -2
  53. package/lib/models/room.js.map +1 -1
  54. package/lib/models/thread.d.ts.map +1 -1
  55. package/lib/models/thread.js +23 -2
  56. package/lib/models/thread.js.map +1 -1
  57. package/lib/rendezvous/MSC4108SignInWithQR.d.ts +3 -3
  58. package/lib/rendezvous/MSC4108SignInWithQR.d.ts.map +1 -1
  59. package/lib/rendezvous/MSC4108SignInWithQR.js.map +1 -1
  60. package/lib/rendezvous/SignInWithQRFlow.d.ts +95 -0
  61. package/lib/rendezvous/SignInWithQRFlow.d.ts.map +1 -0
  62. package/lib/rendezvous/SignInWithQRFlow.js +3 -0
  63. package/lib/rendezvous/SignInWithQRFlow.js.map +1 -0
  64. package/lib/rendezvous/index.d.ts +1 -0
  65. package/lib/rendezvous/index.d.ts.map +1 -1
  66. package/lib/rendezvous/index.js.map +1 -1
  67. package/lib/rust-crypto/index.js +5 -1
  68. package/lib/rust-crypto/index.js.map +1 -1
  69. package/package.json +3 -3
  70. package/src/matrixrtc/CallMembership.ts +85 -95
  71. package/src/matrixrtc/EncryptionManager.ts +6 -4
  72. package/src/matrixrtc/IMembershipManager.ts +2 -15
  73. package/src/matrixrtc/LivekitTransport.ts +58 -9
  74. package/src/matrixrtc/MatrixRTCSession.ts +21 -80
  75. package/src/matrixrtc/MembershipManager.ts +23 -40
  76. package/src/matrixrtc/RTCEncryptionManager.ts +51 -39
  77. package/src/matrixrtc/ToDeviceKeyTransport.ts +7 -7
  78. package/src/matrixrtc/membershipData/rtc.ts +9 -23
  79. package/src/matrixrtc/membershipData/session.ts +14 -2
  80. package/src/matrixrtc/types.ts +6 -0
  81. package/src/models/event-timeline-set.ts +19 -4
  82. package/src/models/room-sticky-events.ts +92 -58
  83. package/src/models/room.ts +8 -2
  84. package/src/models/thread.ts +25 -3
  85. package/src/rendezvous/MSC4108SignInWithQR.ts +3 -3
  86. package/src/rendezvous/SignInWithQRFlow.ts +117 -0
  87. package/src/rendezvous/index.ts +1 -0
  88. package/src/rust-crypto/index.ts +5 -1
@@ -0,0 +1,95 @@
1
+ import { type ClientRendezvousFailureReason, type MSC4108FailureReason, type RendezvousFailureListener } from "./RendezvousFailureReason.ts";
2
+ import { type DeviceAccessTokenResponse, type ValidatedAuthMetadata } from "../oauth/index.ts";
3
+ import type { SecretsBundleJson } from "../@types/matrix-sdk-crypto-wasm.d.ts";
4
+ /**
5
+ * The secrets bundle shared from the existing device to the new device.
6
+ * @experimental Note that this is UNSTABLE and may have breaking changes without notice.
7
+ */
8
+ export type { SecretsBundleJson as SignInWithQRSecrets } from "../@types/matrix-sdk-crypto-wasm.d.ts";
9
+ /**
10
+ * Common interface for the sign in with QR + OAuth2 flows, e.g. the 2024 and 2025 versions of
11
+ * [MSC4108](https://github.com/matrix-org/matrix-spec-proposals/pull/4108).
12
+ * @experimental Note that this is UNSTABLE and may have breaking changes without notice.
13
+ */
14
+ export interface SignInWithQRFlow {
15
+ /**
16
+ * Callback for when the rendezvous fails.
17
+ */
18
+ onFailure?: RendezvousFailureListener;
19
+ /**
20
+ * Returns the check code for the secure channel or undefined if not generated yet.
21
+ */
22
+ readonly checkCode: string | undefined;
23
+ /**
24
+ * Returns the code representing the rendezvous suitable for rendering in a QR code or undefined if not generated yet.
25
+ */
26
+ readonly code: Uint8Array | undefined;
27
+ /**
28
+ * Returns true if the device is the already logged in device reciprocating a new login on the other side of the channel.
29
+ */
30
+ readonly isExistingDevice: boolean;
31
+ /**
32
+ * Returns true if the device is the new device logging in being reciprocated by the device on the other side of the channel.
33
+ */
34
+ readonly isNewDevice: boolean;
35
+ /**
36
+ * Generate the code including doing partial set up of the channel where required.
37
+ */
38
+ generateCode(): Promise<void>;
39
+ /**
40
+ * The first step in the OAuth2 QR login process.
41
+ * To be called after the QR code has been rendered or scanned.
42
+ * The scanning device has to discover the homeserver details, if they scanned the code then they already have it.
43
+ * If the new device is the one rendering the QR code then it has to wait be sent the homeserver details via the rendezvous channel.
44
+ *
45
+ * Depending on the version of the flow, the homeserver is identified either by `serverName` or `baseUrl`.
46
+ */
47
+ negotiateProtocols(): Promise<{
48
+ serverName?: string;
49
+ baseUrl?: string;
50
+ }>;
51
+ /**
52
+ * The second & third step in the OAuth2 QR login process.
53
+ * To be called after `negotiateProtocols` for the existing device.
54
+ * To be called after OAuth2 negotiation for the new device.
55
+ *
56
+ * @param input - Required for the new device to start the device authorization grant, not required for the existing device reciprocating the login
57
+ */
58
+ deviceAuthorizationGrant(input?: {
59
+ metadata: ValidatedAuthMetadata;
60
+ clientId: string;
61
+ deviceId: string;
62
+ }): Promise<{
63
+ verificationUri?: string;
64
+ userCode?: string;
65
+ }>;
66
+ /**
67
+ * The fourth step in the OAuth2 QR login process.
68
+ * The reciprocating device must perform step 5 for this method to resolve.
69
+ * To be called after {@link deviceAuthorizationGrant} only on the new device.
70
+ */
71
+ completeLoginOnNewDevice(input: {
72
+ clientId: string;
73
+ }): Promise<DeviceAccessTokenResponse | undefined>;
74
+ /**
75
+ * The fifth (and final) step in the OAuth2 QR login process.
76
+ * To be called after the new device has completed authentication.
77
+ */
78
+ shareSecrets(): Promise<{
79
+ secrets?: SecretsBundleJson;
80
+ }>;
81
+ /**
82
+ * Decline the login on the existing device.
83
+ */
84
+ declineLoginOnExistingDevice(): Promise<void>;
85
+ /**
86
+ * Cancels the rendezvous session.
87
+ * @param reason the reason for the cancellation
88
+ */
89
+ cancel(reason: MSC4108FailureReason | ClientRendezvousFailureReason): Promise<void>;
90
+ /**
91
+ * Closes the rendezvous session.
92
+ */
93
+ close(): Promise<void>;
94
+ }
95
+ //# sourceMappingURL=SignInWithQRFlow.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SignInWithQRFlow.d.ts","sourceRoot":"","sources":["../../src/rendezvous/SignInWithQRFlow.ts"],"names":[],"mappings":"AAgBA,OAAO,EACH,KAAK,6BAA6B,EAClC,KAAK,oBAAoB,EACzB,KAAK,yBAAyB,EACjC,MAAM,8BAA8B,CAAC;AACtC,OAAO,EAAE,KAAK,yBAAyB,EAAE,KAAK,qBAAqB,EAAE,MAAM,mBAAmB,CAAC;AAC/F,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,uCAAuC,CAAC;AAE/E;;;GAGG;AACH,YAAY,EAAE,iBAAiB,IAAI,mBAAmB,EAAE,MAAM,uCAAuC,CAAC;AAEtG;;;;GAIG;AACH,MAAM,WAAW,gBAAgB;IAC7B;;OAEG;IACH,SAAS,CAAC,EAAE,yBAAyB,CAAC;IAEtC;;OAEG;IACH,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,CAAC;IAEvC;;OAEG;IACH,QAAQ,CAAC,IAAI,EAAE,UAAU,GAAG,SAAS,CAAC;IAEtC;;OAEG;IACH,QAAQ,CAAC,gBAAgB,EAAE,OAAO,CAAC;IAEnC;;OAEG;IACH,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAC;IAE9B;;OAEG;IACH,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAE9B;;;;;;;OAOG;IACH,kBAAkB,IAAI,OAAO,CAAC;QAAE,UAAU,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAEzE;;;;;;OAMG;IACH,wBAAwB,CAAC,KAAK,CAAC,EAAE;QAAE,QAAQ,EAAE,qBAAqB,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC;QAC/G,eAAe,CAAC,EAAE,MAAM,CAAC;QACzB,QAAQ,CAAC,EAAE,MAAM,CAAC;KACrB,CAAC,CAAC;IAEH;;;;OAIG;IACH,wBAAwB,CAAC,KAAK,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,yBAAyB,GAAG,SAAS,CAAC,CAAC;IAEtG;;;OAGG;IACH,YAAY,IAAI,OAAO,CAAC;QAAE,OAAO,CAAC,EAAE,iBAAiB,CAAA;KAAE,CAAC,CAAC;IAEzD;;OAEG;IACH,4BAA4B,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAE9C;;;OAGG;IACH,MAAM,CAAC,MAAM,EAAE,oBAAoB,GAAG,6BAA6B,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEpF;;OAEG;IACH,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CAC1B"}
@@ -0,0 +1,3 @@
1
+ export {};
2
+ export {};
3
+ //# sourceMappingURL=SignInWithQRFlow.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SignInWithQRFlow.js","names":[],"sources":["../../src/rendezvous/SignInWithQRFlow.ts"],"sourcesContent":["/*\nCopyright 2026 Element Creations Ltd.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {\n type ClientRendezvousFailureReason,\n type MSC4108FailureReason,\n type RendezvousFailureListener,\n} from \"./RendezvousFailureReason.ts\";\nimport { type DeviceAccessTokenResponse, type ValidatedAuthMetadata } from \"../oauth/index.ts\";\nimport type { SecretsBundleJson } from \"../@types/matrix-sdk-crypto-wasm.d.ts\";\n\n/**\n * The secrets bundle shared from the existing device to the new device.\n * @experimental Note that this is UNSTABLE and may have breaking changes without notice.\n */\nexport type { SecretsBundleJson as SignInWithQRSecrets } from \"../@types/matrix-sdk-crypto-wasm.d.ts\";\n\n/**\n * Common interface for the sign in with QR + OAuth2 flows, e.g. the 2024 and 2025 versions of\n * [MSC4108](https://github.com/matrix-org/matrix-spec-proposals/pull/4108).\n * @experimental Note that this is UNSTABLE and may have breaking changes without notice.\n */\nexport interface SignInWithQRFlow {\n /**\n * Callback for when the rendezvous fails.\n */\n onFailure?: RendezvousFailureListener;\n\n /**\n * Returns the check code for the secure channel or undefined if not generated yet.\n */\n readonly checkCode: string | undefined;\n\n /**\n * Returns the code representing the rendezvous suitable for rendering in a QR code or undefined if not generated yet.\n */\n readonly code: Uint8Array | undefined;\n\n /**\n * Returns true if the device is the already logged in device reciprocating a new login on the other side of the channel.\n */\n readonly isExistingDevice: boolean;\n\n /**\n * Returns true if the device is the new device logging in being reciprocated by the device on the other side of the channel.\n */\n readonly isNewDevice: boolean;\n\n /**\n * Generate the code including doing partial set up of the channel where required.\n */\n generateCode(): Promise<void>;\n\n /**\n * The first step in the OAuth2 QR login process.\n * To be called after the QR code has been rendered or scanned.\n * The scanning device has to discover the homeserver details, if they scanned the code then they already have it.\n * If the new device is the one rendering the QR code then it has to wait be sent the homeserver details via the rendezvous channel.\n *\n * Depending on the version of the flow, the homeserver is identified either by `serverName` or `baseUrl`.\n */\n negotiateProtocols(): Promise<{ serverName?: string; baseUrl?: string }>;\n\n /**\n * The second & third step in the OAuth2 QR login process.\n * To be called after `negotiateProtocols` for the existing device.\n * To be called after OAuth2 negotiation for the new device.\n *\n * @param input - Required for the new device to start the device authorization grant, not required for the existing device reciprocating the login\n */\n deviceAuthorizationGrant(input?: { metadata: ValidatedAuthMetadata; clientId: string; deviceId: string }): Promise<{\n verificationUri?: string;\n userCode?: string;\n }>;\n\n /**\n * The fourth step in the OAuth2 QR login process.\n * The reciprocating device must perform step 5 for this method to resolve.\n * To be called after {@link deviceAuthorizationGrant} only on the new device.\n */\n completeLoginOnNewDevice(input: { clientId: string }): Promise<DeviceAccessTokenResponse | undefined>;\n\n /**\n * The fifth (and final) step in the OAuth2 QR login process.\n * To be called after the new device has completed authentication.\n */\n shareSecrets(): Promise<{ secrets?: SecretsBundleJson }>;\n\n /**\n * Decline the login on the existing device.\n */\n declineLoginOnExistingDevice(): Promise<void>;\n\n /**\n * Cancels the rendezvous session.\n * @param reason the reason for the cancellation\n */\n cancel(reason: MSC4108FailureReason | ClientRendezvousFailureReason): Promise<void>;\n\n /**\n * Closes the rendezvous session.\n */\n close(): Promise<void>;\n}\n"],"mappings":"","ignoreList":[]}
@@ -2,6 +2,7 @@ import { type MatrixClient } from "../matrix.ts";
2
2
  import { type RendezvousFailureListener } from "./RendezvousFailureReason.ts";
3
3
  import { MSC4108SignInWithQR } from "./MSC4108SignInWithQR.ts";
4
4
  export * from "./MSC4108SignInWithQR.ts";
5
+ export type * from "./SignInWithQRFlow.ts";
5
6
  export type * from "./RendezvousChannel.ts";
6
7
  export type * from "./RendezvousCode.ts";
7
8
  export * from "./RendezvousError.ts";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/rendezvous/index.ts"],"names":[],"mappings":"AAgBA,OAAO,EAAE,KAAK,YAAY,EAA8C,MAAM,cAAc,CAAC;AAC7F,OAAO,EAAwB,KAAK,yBAAyB,EAAE,MAAM,8BAA8B,CAAC;AACpG,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAM/D,cAAc,0BAA0B,CAAC;AACzC,mBAAmB,wBAAwB,CAAC;AAC5C,mBAAmB,qBAAqB,CAAC;AACzC,cAAc,sBAAsB,CAAC;AACrC,cAAc,8BAA8B,CAAC;AAC7C,cAAc,uBAAuB,CAAC;AACtC,mBAAmB,0BAA0B,CAAC;AAC9C,cAAc,uBAAuB,CAAC;AACtC,cAAc,qBAAqB,CAAC;AAEpC;;;;;GAKG;AACH,wBAAsB,uBAAuB,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,OAAO,CAAC,CAgBpF;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,2BAA2B,CAC7C,MAAM,EAAE,YAAY,EACpB,SAAS,EAAE,yBAAyB,EACpC,WAAW,EAAE,WAAW,GACzB,OAAO,CAAC,mBAAmB,CAAC,CAG9B;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,oBAAoB,CACtC,UAAU,EAAE,YAAY,EACxB,SAAS,EAAE,yBAAyB,EACpC,WAAW,EAAE,WAAW,GACzB,OAAO,CAAC,mBAAmB,CAAC,CAM9B"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/rendezvous/index.ts"],"names":[],"mappings":"AAgBA,OAAO,EAAE,KAAK,YAAY,EAA8C,MAAM,cAAc,CAAC;AAC7F,OAAO,EAAwB,KAAK,yBAAyB,EAAE,MAAM,8BAA8B,CAAC;AACpG,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAM/D,cAAc,0BAA0B,CAAC;AACzC,mBAAmB,uBAAuB,CAAC;AAC3C,mBAAmB,wBAAwB,CAAC;AAC5C,mBAAmB,qBAAqB,CAAC;AACzC,cAAc,sBAAsB,CAAC;AACrC,cAAc,8BAA8B,CAAC;AAC7C,cAAc,uBAAuB,CAAC;AACtC,mBAAmB,0BAA0B,CAAC;AAC9C,cAAc,uBAAuB,CAAC;AACtC,cAAc,qBAAqB,CAAC;AAEpC;;;;;GAKG;AACH,wBAAsB,uBAAuB,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,OAAO,CAAC,CAgBpF;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,2BAA2B,CAC7C,MAAM,EAAE,YAAY,EACpB,SAAS,EAAE,yBAAyB,EACpC,WAAW,EAAE,WAAW,GACzB,OAAO,CAAC,mBAAmB,CAAC,CAG9B;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,oBAAoB,CACtC,UAAU,EAAE,YAAY,EACxB,SAAS,EAAE,yBAAyB,EACpC,WAAW,EAAE,WAAW,GACzB,OAAO,CAAC,mBAAmB,CAAC,CAM9B"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../src/rendezvous/index.ts"],"sourcesContent":["/*\nCopyright 2022 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport { type MatrixClient, OAuthGrantType, type ValidatedAuthMetadata } from \"../matrix.ts\";\nimport { MSC4108FailureReason, type RendezvousFailureListener } from \"./RendezvousFailureReason.ts\";\nimport { MSC4108SignInWithQR } from \"./MSC4108SignInWithQR.ts\";\nimport { MSC4108RendezvousSession } from \"./transports/MSC4108RendezvousSession.ts\";\nimport { MSC4108SecureChannel } from \"./channels/MSC4108SecureChannel.ts\";\nimport { RendezvousIntent } from \"./RendezvousIntent.ts\";\nimport { logger } from \"../logger.ts\";\n\nexport * from \"./MSC4108SignInWithQR.ts\";\nexport type * from \"./RendezvousChannel.ts\";\nexport type * from \"./RendezvousCode.ts\";\nexport * from \"./RendezvousError.ts\";\nexport * from \"./RendezvousFailureReason.ts\";\nexport * from \"./RendezvousIntent.ts\";\nexport type * from \"./RendezvousTransport.ts\";\nexport * from \"./transports/index.ts\";\nexport * from \"./channels/index.ts\";\n\n/**\n * Check if the homeserver that the client is connected to supports a variant of sign-in with QR that we can use.\n *\n * @param client the client to check for sign-in with QR support\n * @returns true if the homeserver that the client is connected to supports a variant of sign-in with QR that we can use, false otherwise.\n */\nexport async function isSignInWithQRAvailable(client: MatrixClient): Promise<boolean> {\n let metadata: ValidatedAuthMetadata;\n try {\n metadata = await client.getAuthMetadata();\n } catch (e) {\n logger.warn(\"Failed to fetch auth metadata, assuming sign-in with QR is unavailable\", e);\n return false;\n }\n\n // check for support of device authorization grant\n if (!metadata.grant_types_supported.includes(OAuthGrantType.DeviceAuthorization)) {\n return false;\n }\n\n // check for unstable support for MSC4108 2024 version\n return client.doesServerSupportUnstableFeature(\"org.matrix.msc4108\");\n}\n\n/**\n * Start a linking flow from an existing authenticated client by generating a QR code that can be scanned by the new device.\n * The new device will then authenticate with the server and link itself to the same account as the existing client and\n * share the end-to-end encryption keys.\n *\n * @param client the existing client\n * @param onFailure callback for when the linking process fails\n * @param abortSignal an AbortSignal that can be used to cancel the linking process,\n * for example when the user cancels out of the flow.\n * This will unbind the {@link onFailure} callback and prevent any further steps in the flow from being executed.\n * @returns a promise that resolves to an instance of the linking flow\n */\nexport async function linkNewDeviceByGeneratingQR(\n client: MatrixClient,\n onFailure: RendezvousFailureListener,\n abortSignal: AbortSignal,\n): Promise<MSC4108SignInWithQR> {\n // we assume rust crypto is already initialised\n return initGenerateQrFlow(RendezvousIntent.RECIPROCATE_LOGIN_ON_EXISTING_DEVICE, client, onFailure, abortSignal);\n}\n\n/**\n * Start a sign-in flow by generating a QR code that can be scanned by an existing authenticated client.\n * The existing client will then help complete the authentication of the new device and link it to the same account,\n * sharing the end-to-end encryption keys.\n *\n * @param tempClient temporary client used during the flow for the rendezvous channel\n * @param onFailure callback for when the sign-in process fails\n * @param abortSignal an AbortSignal that can be used to cancel the linking process,\n * for example when the user cancels out of the flow.\n * This will unbind the {@link onFailure} callback and prevent any further steps in the flow from being executed.\n * @returns a promise that resolves to an instance of the sign-in flow\n */\nexport async function signInByGeneratingQR(\n tempClient: MatrixClient,\n onFailure: RendezvousFailureListener,\n abortSignal: AbortSignal,\n): Promise<MSC4108SignInWithQR> {\n // ensure rust crypto is initialized as needed for the secure channel\n const RustSdkCryptoJs = await import(\"@matrix-org/matrix-sdk-crypto-wasm\");\n await RustSdkCryptoJs.initAsync();\n\n return initGenerateQrFlow(RendezvousIntent.LOGIN_ON_NEW_DEVICE, tempClient, onFailure, abortSignal);\n}\n\nasync function initGenerateQrFlow(\n intent: RendezvousIntent,\n client: MatrixClient,\n onFailure: RendezvousFailureListener,\n abortSignal: AbortSignal,\n): Promise<MSC4108SignInWithQR> {\n const session = new MSC4108RendezvousSession({\n onFailure,\n client,\n });\n const channel = new MSC4108SecureChannel(session, undefined, onFailure);\n const flow = new MSC4108SignInWithQR(\n channel,\n false,\n intent === RendezvousIntent.LOGIN_ON_NEW_DEVICE ? undefined : client,\n onFailure,\n );\n\n if (abortSignal.aborted) return flow;\n\n abortSignal.onabort = (): void => {\n // Detach failure handlers\n session.onFailure = undefined;\n channel.onFailure = undefined;\n flow.onFailure = undefined;\n // Cancel the session\n void flow.cancel(MSC4108FailureReason.UserCancelled);\n };\n\n await session.send(\"\"); // open channel\n\n if (!abortSignal.aborted) {\n await flow.generateCode();\n }\n\n return flow;\n}\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../src/rendezvous/index.ts"],"sourcesContent":["/*\nCopyright 2022 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport { type MatrixClient, OAuthGrantType, type ValidatedAuthMetadata } from \"../matrix.ts\";\nimport { MSC4108FailureReason, type RendezvousFailureListener } from \"./RendezvousFailureReason.ts\";\nimport { MSC4108SignInWithQR } from \"./MSC4108SignInWithQR.ts\";\nimport { MSC4108RendezvousSession } from \"./transports/MSC4108RendezvousSession.ts\";\nimport { MSC4108SecureChannel } from \"./channels/MSC4108SecureChannel.ts\";\nimport { RendezvousIntent } from \"./RendezvousIntent.ts\";\nimport { logger } from \"../logger.ts\";\n\nexport * from \"./MSC4108SignInWithQR.ts\";\nexport type * from \"./SignInWithQRFlow.ts\";\nexport type * from \"./RendezvousChannel.ts\";\nexport type * from \"./RendezvousCode.ts\";\nexport * from \"./RendezvousError.ts\";\nexport * from \"./RendezvousFailureReason.ts\";\nexport * from \"./RendezvousIntent.ts\";\nexport type * from \"./RendezvousTransport.ts\";\nexport * from \"./transports/index.ts\";\nexport * from \"./channels/index.ts\";\n\n/**\n * Check if the homeserver that the client is connected to supports a variant of sign-in with QR that we can use.\n *\n * @param client the client to check for sign-in with QR support\n * @returns true if the homeserver that the client is connected to supports a variant of sign-in with QR that we can use, false otherwise.\n */\nexport async function isSignInWithQRAvailable(client: MatrixClient): Promise<boolean> {\n let metadata: ValidatedAuthMetadata;\n try {\n metadata = await client.getAuthMetadata();\n } catch (e) {\n logger.warn(\"Failed to fetch auth metadata, assuming sign-in with QR is unavailable\", e);\n return false;\n }\n\n // check for support of device authorization grant\n if (!metadata.grant_types_supported.includes(OAuthGrantType.DeviceAuthorization)) {\n return false;\n }\n\n // check for unstable support for MSC4108 2024 version\n return client.doesServerSupportUnstableFeature(\"org.matrix.msc4108\");\n}\n\n/**\n * Start a linking flow from an existing authenticated client by generating a QR code that can be scanned by the new device.\n * The new device will then authenticate with the server and link itself to the same account as the existing client and\n * share the end-to-end encryption keys.\n *\n * @param client the existing client\n * @param onFailure callback for when the linking process fails\n * @param abortSignal an AbortSignal that can be used to cancel the linking process,\n * for example when the user cancels out of the flow.\n * This will unbind the {@link onFailure} callback and prevent any further steps in the flow from being executed.\n * @returns a promise that resolves to an instance of the linking flow\n */\nexport async function linkNewDeviceByGeneratingQR(\n client: MatrixClient,\n onFailure: RendezvousFailureListener,\n abortSignal: AbortSignal,\n): Promise<MSC4108SignInWithQR> {\n // we assume rust crypto is already initialised\n return initGenerateQrFlow(RendezvousIntent.RECIPROCATE_LOGIN_ON_EXISTING_DEVICE, client, onFailure, abortSignal);\n}\n\n/**\n * Start a sign-in flow by generating a QR code that can be scanned by an existing authenticated client.\n * The existing client will then help complete the authentication of the new device and link it to the same account,\n * sharing the end-to-end encryption keys.\n *\n * @param tempClient temporary client used during the flow for the rendezvous channel\n * @param onFailure callback for when the sign-in process fails\n * @param abortSignal an AbortSignal that can be used to cancel the linking process,\n * for example when the user cancels out of the flow.\n * This will unbind the {@link onFailure} callback and prevent any further steps in the flow from being executed.\n * @returns a promise that resolves to an instance of the sign-in flow\n */\nexport async function signInByGeneratingQR(\n tempClient: MatrixClient,\n onFailure: RendezvousFailureListener,\n abortSignal: AbortSignal,\n): Promise<MSC4108SignInWithQR> {\n // ensure rust crypto is initialized as needed for the secure channel\n const RustSdkCryptoJs = await import(\"@matrix-org/matrix-sdk-crypto-wasm\");\n await RustSdkCryptoJs.initAsync();\n\n return initGenerateQrFlow(RendezvousIntent.LOGIN_ON_NEW_DEVICE, tempClient, onFailure, abortSignal);\n}\n\nasync function initGenerateQrFlow(\n intent: RendezvousIntent,\n client: MatrixClient,\n onFailure: RendezvousFailureListener,\n abortSignal: AbortSignal,\n): Promise<MSC4108SignInWithQR> {\n const session = new MSC4108RendezvousSession({\n onFailure,\n client,\n });\n const channel = new MSC4108SecureChannel(session, undefined, onFailure);\n const flow = new MSC4108SignInWithQR(\n channel,\n false,\n intent === RendezvousIntent.LOGIN_ON_NEW_DEVICE ? undefined : client,\n onFailure,\n );\n\n if (abortSignal.aborted) return flow;\n\n abortSignal.onabort = (): void => {\n // Detach failure handlers\n session.onFailure = undefined;\n channel.onFailure = undefined;\n flow.onFailure = undefined;\n // Cancel the session\n void flow.cancel(MSC4108FailureReason.UserCancelled);\n };\n\n await session.send(\"\"); // open channel\n\n if (!abortSignal.aborted) {\n await flow.generateCode();\n }\n\n return flow;\n}\n"],"mappings":"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","ignoreList":[]}
@@ -154,7 +154,11 @@ async function initOlmMachine({
154
154
  const roomId = pendingDetails.roomId.toString();
155
155
  if (Date.now() - pendingDetails.inviteAcceptedAtMillis <= MAX_INVITE_ACCEPTANCE_MS_FOR_KEY_BUNDLE) {
156
156
  logger.info(`Checking for pending key bundle for recently-joined room ${roomId} (joined ${new Date(pendingDetails.inviteAcceptedAtMillis).toISOString()})`);
157
- await rustCrypto.maybeAcceptKeyBundle(roomId, pendingDetails.inviterId.toString());
157
+ // Launch `maybeAcceptKeyBundle` without awaiting it so it cannot block crypto initialisation.
158
+ // Failed or interrupted key queries/downloads leave the pending marker for a later startup to retry.
159
+ rustCrypto.maybeAcceptKeyBundle(roomId, pendingDetails.inviterId.toString()).catch(err => {
160
+ logger.error("Error attempting to download a pending room key bundle", err);
161
+ });
158
162
  } else {
159
163
  logger.info(`Clearing pending-key-bundle flag for room ${roomId} (too old: joined ${new Date(pendingDetails.inviteAcceptedAtMillis).toISOString()})`);
160
164
  await olmMachine.clearRoomPendingKeyBundle(new RustSdkCryptoJs.RoomId(roomId));
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../src/rust-crypto/index.ts"],"sourcesContent":["/*\nCopyright 2022 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport * as RustSdkCryptoJs from \"@matrix-org/matrix-sdk-crypto-wasm\";\nimport { StoreHandle } from \"@matrix-org/matrix-sdk-crypto-wasm\";\n\nimport { MAX_INVITE_ACCEPTANCE_MS_FOR_KEY_BUNDLE, RustCrypto } from \"./rust-crypto.ts\";\nimport { type IHttpOpts, type MatrixHttpApi } from \"../http-api/index.ts\";\nimport { type ServerSideSecretStorage } from \"../secret-storage.ts\";\nimport { type Logger } from \"../logger.ts\";\nimport { type CryptoStore, MigrationState } from \"../crypto/store/base.ts\";\nimport {\n migrateFromLegacyCrypto,\n migrateLegacyLocalTrustIfNeeded,\n migrateRoomSettingsFromLegacyCrypto,\n} from \"./libolm_migration.ts\";\nimport { type CryptoCallbacks } from \"../crypto-api/index.ts\";\n\n/**\n * The arguments used to initialise RustCrypto, passed in to initRustCrypto.\n *\n * @internal\n */\nexport interface InitRustCryptoArgs {\n /** A `Logger` instance that will be used for debug output. */\n logger: Logger;\n\n /**\n * Low-level HTTP interface: used to make outgoing requests required by the rust SDK.\n * We expect it to set the access token, etc.\n */\n http: MatrixHttpApi<IHttpOpts & { onlyData: true }>;\n\n /** The local user's User ID. */\n userId: string;\n\n /** The local user's Device ID. */\n deviceId: string;\n\n /** Interface to server-side secret storage. */\n secretStorage: ServerSideSecretStorage;\n\n /** Crypto callbacks provided by the application. */\n cryptoCallbacks: CryptoCallbacks;\n\n /**\n * The prefix to use on the indexeddbs created by rust-crypto.\n * If `null`, a memory store will be used.\n */\n storePrefix: string | null;\n\n /**\n * A passphrase to use to encrypt the indexeddb created by rust-crypto.\n *\n * Ignored if `storePrefix` is null, or `storeKey` is set. If neither this nor `storeKey` is set\n * (and `storePrefix` is not null), the indexeddb will be unencrypted.\n */\n storePassphrase?: string;\n\n /**\n * A key to use to encrypt the indexeddb created by rust-crypto.\n *\n * Ignored if `storePrefix` is null. Otherwise, if it is set, it must be a 32-byte cryptographic key, which\n * will be used to encrypt the indexeddb. See also `storePassphrase`.\n */\n storeKey?: Uint8Array;\n\n /** If defined, we will check if any data needs migrating from this store to the rust store. */\n legacyCryptoStore?: CryptoStore;\n\n /** The pickle key for `legacyCryptoStore` */\n legacyPickleKey?: string;\n\n /**\n * A callback which will receive progress updates on migration from `legacyCryptoStore`.\n *\n * Called with (-1, -1) to mark the end of migration.\n */\n legacyMigrationProgressListener?: (progress: number, total: number) => void;\n\n /**\n * Whether to enable support for encrypting state events.\n */\n enableEncryptedStateEvents?: boolean;\n\n /**\n * Optional PEM-formatted string that provides CA certificates. These will\n * be used to check X.509 signatures on user identities. Any user identity\n * that has a valid signature according to the supplied CAs will be\n * considered verified, without any manual verification taking place.\n */\n caCertsPem?: string;\n\n /**\n * Optional async function for signing some data with an X.509 certificate.\n * Used to sign the user's identity so compatible clients will recognise\n * this user as verified without manual verification taking place. If you\n * supply this you must also supply {@link InitRustCryptoArgs#x509Validity}.\n */\n x509Signer?: (item: Uint8Array) => Promise<{\n signature_bytes: Uint8Array;\n certificate_chain: string;\n signature_scheme: \"RsaPssSha512\";\n }>;\n\n /**\n * Optional function returning the validity period of the X.509 certificate\n * used for signing, as the number of milliseconds since the Unix epoch. If\n * you supply this you must also supply {@link InitRustCryptoArgs#x509Signer}.\n */\n x509Validity?: () => number;\n}\n\n/**\n * Create a new `RustCrypto` implementation\n *\n * @param args - InitRustCryptoArgs\n * @internal\n */\nexport async function initRustCrypto(args: InitRustCryptoArgs): Promise<RustCrypto> {\n const { logger } = args;\n\n // initialise the rust matrix-sdk-crypto-wasm, if it hasn't already been done\n logger.debug(\"Initialising Rust crypto-sdk WASM artifact\");\n await RustSdkCryptoJs.initAsync();\n\n logger.debug(\"Opening Rust CryptoStore\");\n let storeHandle;\n if (args.storePrefix) {\n if (args.storeKey) {\n storeHandle = await StoreHandle.openWithKey(args.storePrefix, args.storeKey, logger);\n } else {\n storeHandle = await StoreHandle.open(args.storePrefix, args.storePassphrase, logger);\n }\n } else {\n storeHandle = await StoreHandle.open(null, null, logger);\n }\n\n if (args.legacyCryptoStore) {\n // We have a legacy crypto store, which we may need to migrate from.\n await migrateFromLegacyCrypto({\n legacyStore: args.legacyCryptoStore,\n storeHandle,\n ...args,\n });\n }\n\n const rustCrypto = await initOlmMachine(args, storeHandle);\n\n storeHandle.free();\n\n logger.debug(\"Completed rust crypto-sdk setup\");\n return rustCrypto;\n}\n\nasync function initOlmMachine(\n {\n logger,\n http,\n userId,\n deviceId,\n secretStorage,\n cryptoCallbacks,\n legacyCryptoStore,\n enableEncryptedStateEvents,\n caCertsPem,\n x509Signer,\n x509Validity,\n }: InitRustCryptoArgs,\n storeHandle: StoreHandle,\n): Promise<RustCrypto> {\n logger.debug(\"Init OlmMachine\");\n\n const olmMachine = await RustSdkCryptoJs.OlmMachine.initFromStore(\n new RustSdkCryptoJs.UserId(userId),\n new RustSdkCryptoJs.DeviceId(deviceId),\n storeHandle,\n logger,\n caCertsPem,\n x509Signer,\n x509Validity,\n );\n\n // A final migration step, now that we have an OlmMachine.\n if (legacyCryptoStore) {\n await migrateRoomSettingsFromLegacyCrypto({\n logger,\n legacyStore: legacyCryptoStore,\n olmMachine,\n });\n }\n\n // Disable room key requests, per https://github.com/vector-im/element-web/issues/26524.\n olmMachine.roomKeyRequestsEnabled = false;\n\n const rustCrypto = new RustCrypto(\n logger,\n olmMachine,\n http,\n userId,\n deviceId,\n secretStorage,\n cryptoCallbacks,\n enableEncryptedStateEvents,\n );\n\n olmMachine.registerRoomKeyUpdatedCallback((sessions: RustSdkCryptoJs.RoomKeyInfo[]) =>\n rustCrypto.onRoomKeysUpdated(sessions),\n );\n olmMachine.registerRoomKeysWithheldCallback((withheld: RustSdkCryptoJs.RoomKeyWithheldInfo[]) =>\n rustCrypto.onRoomKeysWithheld(withheld),\n );\n olmMachine.registerUserIdentityUpdatedCallback((userId: RustSdkCryptoJs.UserId) =>\n rustCrypto.onUserIdentityUpdated(userId),\n );\n olmMachine.registerDevicesUpdatedCallback((userIds: string[]) => rustCrypto.onDevicesUpdated(userIds));\n\n // Check if there are any key backup secrets pending processing. There may be multiple secrets to process if several devices have gossiped them.\n // The `registerReceiveSecretCallback` function will only be triggered for new secrets. If the client is restarted before processing them, the secrets will need to be manually handled.\n void rustCrypto.checkSecrets(\"m.megolm_backup.v1\");\n\n // Register a callback to be notified when a new secret is received, as for now only the key backup secret is supported (the cross signing secrets are handled automatically by the OlmMachine)\n olmMachine.registerReceiveSecretCallback((name: string, _value: string) =>\n // Instead of directly checking the secret value, we poll the inbox to get all values for that secret type.\n // Once we have all the values, we can safely clear the secret inbox.\n rustCrypto.checkSecrets(name),\n );\n\n // Tell the OlmMachine to think about its outgoing requests before we hand control back to the application.\n //\n // This is primarily a fudge to get it to correctly populate the `users_for_key_query` list, so that future\n // calls to getIdentity (etc) block until the key queries are performed.\n //\n // Note that we don't actually need to *make* any requests here; it is sufficient to tell the Rust side to think\n // about them.\n //\n // XXX: find a less hacky way to do this.\n await olmMachine.outgoingRequests();\n\n if (legacyCryptoStore && (await legacyCryptoStore.containsData())) {\n const migrationState = await legacyCryptoStore.getMigrationState();\n if (migrationState < MigrationState.INITIAL_OWN_KEY_QUERY_DONE) {\n logger.debug(`Performing initial key query after migration`);\n // We need to do an initial keys query so that the rust stack can properly update trust of\n // the user device and identity from the migrated private keys.\n // If not done, there is a short period where the own device/identity trust will be undefined after migration.\n let initialKeyQueryDone = false;\n while (!initialKeyQueryDone) {\n try {\n await rustCrypto.userHasCrossSigningKeys(userId);\n initialKeyQueryDone = true;\n } catch (e) {\n // If the initial key query fails, we retry until it succeeds.\n logger.error(\"Failed to check for cross-signing keys after migration, retrying\", e);\n }\n }\n\n // If the private master cross-signing key was not cached in the legacy store, the rust session\n // will not be able to establish the trust of the user identity.\n // That means that after migration the session could revert to unverified.\n // In order to avoid asking the users to re-verify their sessions, we need to migrate the legacy local trust\n // (if the legacy session was already verified) to the new session.\n await migrateLegacyLocalTrustIfNeeded({ legacyCryptoStore, rustCrypto, logger });\n\n await legacyCryptoStore.setMigrationState(MigrationState.INITIAL_OWN_KEY_QUERY_DONE);\n }\n }\n\n // If we have any recently-joined rooms, see if we have a pending key bundle for them.\n for (const pendingDetails of await olmMachine.getAllRoomsPendingKeyBundles()) {\n const roomId = pendingDetails.roomId.toString();\n if (Date.now() - pendingDetails.inviteAcceptedAtMillis <= MAX_INVITE_ACCEPTANCE_MS_FOR_KEY_BUNDLE) {\n logger.info(\n `Checking for pending key bundle for recently-joined room ${roomId} (joined ${new Date(pendingDetails.inviteAcceptedAtMillis).toISOString()})`,\n );\n await rustCrypto.maybeAcceptKeyBundle(roomId, pendingDetails.inviterId.toString());\n } else {\n logger.info(\n `Clearing pending-key-bundle flag for room ${roomId} (too old: joined ${new Date(pendingDetails.inviteAcceptedAtMillis).toISOString()})`,\n );\n await olmMachine.clearRoomPendingKeyBundle(new RustSdkCryptoJs.RoomId(roomId));\n }\n }\n\n return rustCrypto;\n}\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../src/rust-crypto/index.ts"],"sourcesContent":["/*\nCopyright 2022 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport * as RustSdkCryptoJs from \"@matrix-org/matrix-sdk-crypto-wasm\";\nimport { StoreHandle } from \"@matrix-org/matrix-sdk-crypto-wasm\";\n\nimport { MAX_INVITE_ACCEPTANCE_MS_FOR_KEY_BUNDLE, RustCrypto } from \"./rust-crypto.ts\";\nimport { type IHttpOpts, type MatrixHttpApi } from \"../http-api/index.ts\";\nimport { type ServerSideSecretStorage } from \"../secret-storage.ts\";\nimport { type Logger } from \"../logger.ts\";\nimport { type CryptoStore, MigrationState } from \"../crypto/store/base.ts\";\nimport {\n migrateFromLegacyCrypto,\n migrateLegacyLocalTrustIfNeeded,\n migrateRoomSettingsFromLegacyCrypto,\n} from \"./libolm_migration.ts\";\nimport { type CryptoCallbacks } from \"../crypto-api/index.ts\";\n\n/**\n * The arguments used to initialise RustCrypto, passed in to initRustCrypto.\n *\n * @internal\n */\nexport interface InitRustCryptoArgs {\n /** A `Logger` instance that will be used for debug output. */\n logger: Logger;\n\n /**\n * Low-level HTTP interface: used to make outgoing requests required by the rust SDK.\n * We expect it to set the access token, etc.\n */\n http: MatrixHttpApi<IHttpOpts & { onlyData: true }>;\n\n /** The local user's User ID. */\n userId: string;\n\n /** The local user's Device ID. */\n deviceId: string;\n\n /** Interface to server-side secret storage. */\n secretStorage: ServerSideSecretStorage;\n\n /** Crypto callbacks provided by the application. */\n cryptoCallbacks: CryptoCallbacks;\n\n /**\n * The prefix to use on the indexeddbs created by rust-crypto.\n * If `null`, a memory store will be used.\n */\n storePrefix: string | null;\n\n /**\n * A passphrase to use to encrypt the indexeddb created by rust-crypto.\n *\n * Ignored if `storePrefix` is null, or `storeKey` is set. If neither this nor `storeKey` is set\n * (and `storePrefix` is not null), the indexeddb will be unencrypted.\n */\n storePassphrase?: string;\n\n /**\n * A key to use to encrypt the indexeddb created by rust-crypto.\n *\n * Ignored if `storePrefix` is null. Otherwise, if it is set, it must be a 32-byte cryptographic key, which\n * will be used to encrypt the indexeddb. See also `storePassphrase`.\n */\n storeKey?: Uint8Array;\n\n /** If defined, we will check if any data needs migrating from this store to the rust store. */\n legacyCryptoStore?: CryptoStore;\n\n /** The pickle key for `legacyCryptoStore` */\n legacyPickleKey?: string;\n\n /**\n * A callback which will receive progress updates on migration from `legacyCryptoStore`.\n *\n * Called with (-1, -1) to mark the end of migration.\n */\n legacyMigrationProgressListener?: (progress: number, total: number) => void;\n\n /**\n * Whether to enable support for encrypting state events.\n */\n enableEncryptedStateEvents?: boolean;\n\n /**\n * Optional PEM-formatted string that provides CA certificates. These will\n * be used to check X.509 signatures on user identities. Any user identity\n * that has a valid signature according to the supplied CAs will be\n * considered verified, without any manual verification taking place.\n */\n caCertsPem?: string;\n\n /**\n * Optional async function for signing some data with an X.509 certificate.\n * Used to sign the user's identity so compatible clients will recognise\n * this user as verified without manual verification taking place. If you\n * supply this you must also supply {@link InitRustCryptoArgs#x509Validity}.\n */\n x509Signer?: (item: Uint8Array) => Promise<{\n signature_bytes: Uint8Array;\n certificate_chain: string;\n signature_scheme: \"RsaPssSha512\";\n }>;\n\n /**\n * Optional function returning the validity period of the X.509 certificate\n * used for signing, as the number of milliseconds since the Unix epoch. If\n * you supply this you must also supply {@link InitRustCryptoArgs#x509Signer}.\n */\n x509Validity?: () => number;\n}\n\n/**\n * Create a new `RustCrypto` implementation\n *\n * @param args - InitRustCryptoArgs\n * @internal\n */\nexport async function initRustCrypto(args: InitRustCryptoArgs): Promise<RustCrypto> {\n const { logger } = args;\n\n // initialise the rust matrix-sdk-crypto-wasm, if it hasn't already been done\n logger.debug(\"Initialising Rust crypto-sdk WASM artifact\");\n await RustSdkCryptoJs.initAsync();\n\n logger.debug(\"Opening Rust CryptoStore\");\n let storeHandle;\n if (args.storePrefix) {\n if (args.storeKey) {\n storeHandle = await StoreHandle.openWithKey(args.storePrefix, args.storeKey, logger);\n } else {\n storeHandle = await StoreHandle.open(args.storePrefix, args.storePassphrase, logger);\n }\n } else {\n storeHandle = await StoreHandle.open(null, null, logger);\n }\n\n if (args.legacyCryptoStore) {\n // We have a legacy crypto store, which we may need to migrate from.\n await migrateFromLegacyCrypto({\n legacyStore: args.legacyCryptoStore,\n storeHandle,\n ...args,\n });\n }\n\n const rustCrypto = await initOlmMachine(args, storeHandle);\n\n storeHandle.free();\n\n logger.debug(\"Completed rust crypto-sdk setup\");\n return rustCrypto;\n}\n\nasync function initOlmMachine(\n {\n logger,\n http,\n userId,\n deviceId,\n secretStorage,\n cryptoCallbacks,\n legacyCryptoStore,\n enableEncryptedStateEvents,\n caCertsPem,\n x509Signer,\n x509Validity,\n }: InitRustCryptoArgs,\n storeHandle: StoreHandle,\n): Promise<RustCrypto> {\n logger.debug(\"Init OlmMachine\");\n\n const olmMachine = await RustSdkCryptoJs.OlmMachine.initFromStore(\n new RustSdkCryptoJs.UserId(userId),\n new RustSdkCryptoJs.DeviceId(deviceId),\n storeHandle,\n logger,\n caCertsPem,\n x509Signer,\n x509Validity,\n );\n\n // A final migration step, now that we have an OlmMachine.\n if (legacyCryptoStore) {\n await migrateRoomSettingsFromLegacyCrypto({\n logger,\n legacyStore: legacyCryptoStore,\n olmMachine,\n });\n }\n\n // Disable room key requests, per https://github.com/vector-im/element-web/issues/26524.\n olmMachine.roomKeyRequestsEnabled = false;\n\n const rustCrypto = new RustCrypto(\n logger,\n olmMachine,\n http,\n userId,\n deviceId,\n secretStorage,\n cryptoCallbacks,\n enableEncryptedStateEvents,\n );\n\n olmMachine.registerRoomKeyUpdatedCallback((sessions: RustSdkCryptoJs.RoomKeyInfo[]) =>\n rustCrypto.onRoomKeysUpdated(sessions),\n );\n olmMachine.registerRoomKeysWithheldCallback((withheld: RustSdkCryptoJs.RoomKeyWithheldInfo[]) =>\n rustCrypto.onRoomKeysWithheld(withheld),\n );\n olmMachine.registerUserIdentityUpdatedCallback((userId: RustSdkCryptoJs.UserId) =>\n rustCrypto.onUserIdentityUpdated(userId),\n );\n olmMachine.registerDevicesUpdatedCallback((userIds: string[]) => rustCrypto.onDevicesUpdated(userIds));\n\n // Check if there are any key backup secrets pending processing. There may be multiple secrets to process if several devices have gossiped them.\n // The `registerReceiveSecretCallback` function will only be triggered for new secrets. If the client is restarted before processing them, the secrets will need to be manually handled.\n void rustCrypto.checkSecrets(\"m.megolm_backup.v1\");\n\n // Register a callback to be notified when a new secret is received, as for now only the key backup secret is supported (the cross signing secrets are handled automatically by the OlmMachine)\n olmMachine.registerReceiveSecretCallback((name: string, _value: string) =>\n // Instead of directly checking the secret value, we poll the inbox to get all values for that secret type.\n // Once we have all the values, we can safely clear the secret inbox.\n rustCrypto.checkSecrets(name),\n );\n\n // Tell the OlmMachine to think about its outgoing requests before we hand control back to the application.\n //\n // This is primarily a fudge to get it to correctly populate the `users_for_key_query` list, so that future\n // calls to getIdentity (etc) block until the key queries are performed.\n //\n // Note that we don't actually need to *make* any requests here; it is sufficient to tell the Rust side to think\n // about them.\n //\n // XXX: find a less hacky way to do this.\n await olmMachine.outgoingRequests();\n\n if (legacyCryptoStore && (await legacyCryptoStore.containsData())) {\n const migrationState = await legacyCryptoStore.getMigrationState();\n if (migrationState < MigrationState.INITIAL_OWN_KEY_QUERY_DONE) {\n logger.debug(`Performing initial key query after migration`);\n // We need to do an initial keys query so that the rust stack can properly update trust of\n // the user device and identity from the migrated private keys.\n // If not done, there is a short period where the own device/identity trust will be undefined after migration.\n let initialKeyQueryDone = false;\n while (!initialKeyQueryDone) {\n try {\n await rustCrypto.userHasCrossSigningKeys(userId);\n initialKeyQueryDone = true;\n } catch (e) {\n // If the initial key query fails, we retry until it succeeds.\n logger.error(\"Failed to check for cross-signing keys after migration, retrying\", e);\n }\n }\n\n // If the private master cross-signing key was not cached in the legacy store, the rust session\n // will not be able to establish the trust of the user identity.\n // That means that after migration the session could revert to unverified.\n // In order to avoid asking the users to re-verify their sessions, we need to migrate the legacy local trust\n // (if the legacy session was already verified) to the new session.\n await migrateLegacyLocalTrustIfNeeded({ legacyCryptoStore, rustCrypto, logger });\n\n await legacyCryptoStore.setMigrationState(MigrationState.INITIAL_OWN_KEY_QUERY_DONE);\n }\n }\n\n // If we have any recently-joined rooms, see if we have a pending key bundle for them.\n for (const pendingDetails of await olmMachine.getAllRoomsPendingKeyBundles()) {\n const roomId = pendingDetails.roomId.toString();\n if (Date.now() - pendingDetails.inviteAcceptedAtMillis <= MAX_INVITE_ACCEPTANCE_MS_FOR_KEY_BUNDLE) {\n logger.info(\n `Checking for pending key bundle for recently-joined room ${roomId} (joined ${new Date(pendingDetails.inviteAcceptedAtMillis).toISOString()})`,\n );\n // Launch `maybeAcceptKeyBundle` without awaiting it so it cannot block crypto initialisation.\n // Failed or interrupted key queries/downloads leave the pending marker for a later startup to retry.\n rustCrypto.maybeAcceptKeyBundle(roomId, pendingDetails.inviterId.toString()).catch((err) => {\n logger.error(\"Error attempting to download a pending room key bundle\", err);\n });\n } else {\n logger.info(\n `Clearing pending-key-bundle flag for room ${roomId} (too old: joined ${new Date(pendingDetails.inviteAcceptedAtMillis).toISOString()})`,\n );\n await olmMachine.clearRoomPendingKeyBundle(new RustSdkCryptoJs.RoomId(roomId));\n }\n }\n\n return rustCrypto;\n}\n"],"mappings":"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","ignoreList":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "matrix-js-sdk",
3
- "version": "43.0.0",
3
+ "version": "44.0.0",
4
4
  "description": "Matrix Client-Server SDK for Javascript",
5
5
  "engines": {
6
6
  "node": ">=22.0.0"
@@ -38,7 +38,7 @@
38
38
  ],
39
39
  "dependencies": {
40
40
  "@babel/runtime": "^8.0.0",
41
- "@matrix-org/matrix-sdk-crypto-wasm": "^18.7.0",
41
+ "@matrix-org/matrix-sdk-crypto-wasm": "^18.9.0",
42
42
  "another-json": "^0.2.0",
43
43
  "bs58": "^6.0.0",
44
44
  "content-type": "^3.0.0",
@@ -79,7 +79,7 @@
79
79
  "knip": "^6.0.0",
80
80
  "lint-staged": "^17.0.0",
81
81
  "matrix-mock-request": "^2.5.0",
82
- "oxfmt": "^0.67.0",
82
+ "oxfmt": "^0.70.0",
83
83
  "oxlint": "^1.70.0",
84
84
  "oxlint-tsgolint": "^7.0.0",
85
85
  "typedoc": "^0.28.1",
@@ -15,7 +15,7 @@ limitations under the License.
15
15
  */
16
16
 
17
17
  import { deepCompare } from "../utils.ts";
18
- import { type RTCCallIntent, type Transport, type SlotDescription } from "./types.ts";
18
+ import { type RTCCallCapability, type RTCCallIntent, type Transport, type SlotDescription } from "./types.ts";
19
19
  import { type MatrixEvent } from "../models/event.ts";
20
20
  import { type Logger, logger } from "../logger.ts";
21
21
  import { computeSlotId, slotIdToDescription } from "./utils.ts";
@@ -28,6 +28,7 @@ import {
28
28
  MatrixRTCMembershipParseError,
29
29
  } from "./membershipData/index.ts";
30
30
  import { EventType } from "../@types/event.ts";
31
+ import { isUnstableLivekitTransport } from "./LivekitTransport.ts";
31
32
 
32
33
  /**
33
34
  * The default duration in milliseconds that a membership is considered valid for.
@@ -68,6 +69,41 @@ type MembershipData =
68
69
  | { kind: MembershipKind.RTC; data: RtcMembershipData }
69
70
  | { kind: MembershipKind.Session; data: SessionMembershipData };
70
71
 
72
+ function getDeviceId({ kind, data }: MembershipData): string {
73
+ switch (kind) {
74
+ case MembershipKind.RTC:
75
+ return data.member.device_id;
76
+ case MembershipKind.Session:
77
+ return data.device_id;
78
+ }
79
+ }
80
+
81
+ function getMemberId({ kind, data }: MembershipData, sender: string): string {
82
+ // the createdTs behaves equivalent to the membershipID.
83
+ // we only need the field for the legacy member events where we needed to update them
84
+ // synapse ignores sending state events if they have the same content.
85
+ switch (kind) {
86
+ case MembershipKind.RTC:
87
+ return data.member.id;
88
+ case MembershipKind.Session:
89
+ return (
90
+ // best case we have a client already publishing the right custom membershipId
91
+ data.membershipID ??
92
+ // alternatively we use the hardcoded jwt id default value (used until version 0.16.0)
93
+ `${sender}:${data.device_id}`
94
+ );
95
+ }
96
+ }
97
+
98
+ function getTransports({ kind, data }: MembershipData): Transport[] {
99
+ switch (kind) {
100
+ case MembershipKind.RTC:
101
+ return data.transports.published;
102
+ case MembershipKind.Session:
103
+ return data.foci_preferred;
104
+ }
105
+ }
106
+
71
107
  type LimitedEvent = Pick<MatrixEvent, "getId" | "getSender" | "getTs" | "getType" | "getContent">;
72
108
  // TODO: Rename to RtcMembership once we removed the legacy SessionMembership is removed, to avoid confusion.
73
109
  export class CallMembership {
@@ -104,16 +140,22 @@ export class CallMembership {
104
140
  * @param matrixEvent The Matrix event to read.
105
141
  */
106
142
  public static async parseFromEvent(matrixEvent: LimitedEvent): Promise<CallMembership> {
143
+ const sender = matrixEvent.getSender();
144
+ if (sender === undefined) throw new Error("matrixEvent is missing sender field");
107
145
  const membershipData: MembershipData = this.membershipDataFromMatrixEvent(matrixEvent);
108
- const rtcBackendIdentity =
109
- membershipData.kind === MembershipKind.RTC
110
- ? await computeRtcIdentityRaw(
111
- membershipData.data.member.user_id,
112
- membershipData.data.member.device_id,
113
- membershipData.data.member.id,
114
- )
115
- : `${matrixEvent.getSender()}:${membershipData.data.device_id}`;
116
- return new CallMembership(matrixEvent, membershipData, rtcBackendIdentity);
146
+ const livekitTransports = getTransports(membershipData).filter(isUnstableLivekitTransport);
147
+ const legacyIdentity = `${sender}:${getDeviceId(membershipData)}`;
148
+
149
+ const backendIdentities: string[] = [];
150
+ if (livekitTransports.some((t) => "url" in t)) {
151
+ backendIdentities.push(await computeRtcIdentityRaw(sender, getMemberId(membershipData, sender)));
152
+ }
153
+ if (livekitTransports.some((t) => "livekit_service_url" in t)) {
154
+ backendIdentities.push(legacyIdentity);
155
+ }
156
+
157
+ // rtcBackendIdentity is deprecated, so it's fine to always set it to the legacy identity.
158
+ return new CallMembership(matrixEvent, membershipData, backendIdentities, legacyIdentity);
117
159
  }
118
160
 
119
161
  public static equal(a?: CallMembership, b?: CallMembership): boolean {
@@ -131,21 +173,27 @@ export class CallMembership {
131
173
  * Use `parseFromEvent`.
132
174
  * Constructor should only be used by tests.
133
175
  * @private
134
- * @param matrixEvent
135
- * @param membershipData
136
- * @param rtcBackendIdentity
137
176
  */
138
177
  public constructor(
139
178
  /** The Matrix event that this membership is based on */
140
179
  private readonly matrixEvent: LimitedEvent,
141
180
  private readonly membershipData: MembershipData,
181
+ /**
182
+ * Possible identities which this member may have on the backends of
183
+ * their published transports.
184
+ */
185
+ public readonly backendIdentities: string[],
186
+ /**
187
+ * @deprecated Check the backend for the existence of any of
188
+ * {@link backendIdentities} instead.
189
+ */
142
190
  public readonly rtcBackendIdentity: string,
143
191
  ) {
144
192
  const eventId = matrixEvent.getId();
145
193
  const sender = matrixEvent.getSender();
146
194
 
147
- if (eventId === undefined) throw new Error("parentEvent is missing eventId field");
148
- if (sender === undefined) throw new Error("parentEvent is missing sender field");
195
+ if (eventId === undefined) throw new Error("matrixEvent is missing eventId field");
196
+ if (sender === undefined) throw new Error("matrixEvent is missing sender field");
149
197
 
150
198
  this.logger = logger.getChild(`[CallMembership ${sender}:${this.deviceId}]`);
151
199
  this.matrixEventData = { eventId, sender };
@@ -157,14 +205,7 @@ export class CallMembership {
157
205
  }
158
206
 
159
207
  public get userId(): string {
160
- const { kind, data } = this.membershipData;
161
- switch (kind) {
162
- case MembershipKind.RTC:
163
- return data.member.user_id;
164
- case MembershipKind.Session:
165
- default:
166
- return this.matrixEventData.sender;
167
- }
208
+ return this.matrixEventData.sender;
168
209
  }
169
210
 
170
211
  public get eventId(): string {
@@ -226,14 +267,7 @@ export class CallMembership {
226
267
  }
227
268
 
228
269
  public get deviceId(): string {
229
- const { kind, data } = this.membershipData;
230
- switch (kind) {
231
- case MembershipKind.RTC:
232
- return data.member.device_id;
233
- case MembershipKind.Session:
234
- default:
235
- return data.device_id;
236
- }
270
+ return getDeviceId(this.membershipData);
237
271
  }
238
272
 
239
273
  public get callIntent(): RTCCallIntent | undefined {
@@ -245,6 +279,14 @@ export class CallMembership {
245
279
  return undefined;
246
280
  }
247
281
 
282
+ public get callCapabilities(): RTCCallCapability[] | undefined {
283
+ const capabilities = this.applicationData["capabilities"];
284
+ if (capabilities === undefined) return undefined;
285
+ if (Array.isArray(capabilities) && capabilities.every((c) => typeof c === "string")) return capabilities;
286
+ this.logger.warn("RTC membership has invalid capabilities");
287
+ return undefined;
288
+ }
289
+
248
290
  /**
249
291
  * Parsed `slot_id` (format `{application}#{id}`) into its components (application and id).
250
292
  */
@@ -257,14 +299,6 @@ export class CallMembership {
257
299
  return slotIdToDescription(this.slotId);
258
300
  }
259
301
 
260
- /**
261
- * The application `type`.
262
- * @deprecated Use @see applicationData
263
- */
264
- public get application(): string {
265
- return this.applicationData.type;
266
- }
267
-
268
302
  /**
269
303
  * Information about the application being used for the RTC session.
270
304
  * May contain extra keys specific to the application.
@@ -301,8 +335,8 @@ export class CallMembership {
301
335
  /**
302
336
  * This computes the membership ID for the membership.
303
337
  * For the sticky event based rtcSessionData this is trivial it is `member.id`.
304
- * This is not supposed to be used to identity on an rtc backend. This is just a nouance for
305
- * a generated (sha256) anonymised identity. Only send `rtcBackendIdentity` to any rtc backend service.
338
+ * This is not supposed to be used to identity on an rtc backend. This is just a nuance for
339
+ * a generated (sha256) anonymised identity. Only send {@link backendIdentities} to any rtc backend service.
306
340
  *
307
341
  * For the legacy sessionMemberEvents it is a bit more complex. Here we sometimes do not have this data
308
342
  * in the event content and we expected the SFU and the client to use `${this.matrixEventData.sender}:${data.device_id}`.
@@ -313,37 +347,14 @@ export class CallMembership {
313
347
  * It is also possible for a session event to set a custom membershipID. in that case this will be used.
314
348
  */
315
349
  public get memberId(): string {
316
- // the createdTs behaves equivalent to the membershipID.
317
- // we only need the field for the legacy member events where we needed to update them
318
- // synapse ignores sending state events if they have the same content.
319
- const { kind, data } = this.membershipData;
320
- switch (kind) {
321
- case "rtc":
322
- return data.member.id;
323
- case "session":
324
- return (
325
- // best case we have a client already publishing the right custom membershipId
326
- data.membershipID ??
327
- // alternativly we use the hard coded jwt id defuatl value (used until version 0.16.0)
328
- `${this.matrixEventData.sender}:${data.device_id}`
329
- );
330
- default:
331
- throw Error("Not possible to get memberID without knowing the membership event kind");
332
- }
333
- }
334
-
335
- /**
336
- * @deprecated renamed to `memberId`
337
- */
338
- public get membershipID(): string {
339
- return this.memberId;
350
+ return getMemberId(this.membershipData, this.matrixEventData.sender);
340
351
  }
341
352
 
342
353
  public createdTs(): number {
343
354
  const { kind, data } = this.membershipData;
344
355
  switch (kind) {
345
356
  case MembershipKind.RTC:
346
- // TODO we need to read the referenced (relation) event if available to get the real created_ts
357
+ // TODO Do we need this to represent the TS of the original join?
347
358
  return this.matrixEvent.getTs();
348
359
  case MembershipKind.Session:
349
360
  default:
@@ -406,34 +417,20 @@ export class CallMembership {
406
417
  * Directly relates to the `transports.published` field.
407
418
  *
408
419
  * ## Legacy session membership
409
- * In case of a legacy session membership (m.call.member) this will return the selected transport where
410
- * media is published. How this selection happens depends on the `focus_active` field of the session membership.
411
- * If the `focus_selection` is `oldest_membership` this will return the transport of the oldest membership
412
- * in the room (based on the `created_ts` field of the session membership).
413
- * If the `focus_selection` is `multi_sfu` it will return the first transport of the `foci_preferred` list.
414
- * (`multi_sfu` is equivalent to how `m.rtc.member` `transports.published` work).
415
- * @param oldestMembership For backwards compatibility with session membership (legacy). Unused in case of RTC membership.
416
- * Always required to make the consumer not care if it deals with RTC or session memberships.
420
+ * In case of a legacy session membership (m.call.member) this will return the first transport of the
421
+ * `foci_preferred` list. (`multi_sfu` is equivalent to how `m.rtc.member` `transports.published` work).
422
+ *
423
+ * @param _oldestMembership Deprecated and unused.
424
+ *
417
425
  * @returns The transport this membership uses to publish media or undefined if no transport is available.
418
426
  */
419
- public getTransport(oldestMembership: CallMembership): Transport | undefined {
427
+ public getTransport(_oldestMembership?: CallMembership): Transport | undefined {
420
428
  const { kind, data } = this.membershipData;
421
429
  switch (kind) {
422
430
  case MembershipKind.RTC:
423
431
  return data.transports.published[0];
424
432
  case MembershipKind.Session:
425
- switch (data.focus_active.focus_selection) {
426
- case "oldest_membership":
427
- if (CallMembership.equal(this, oldestMembership)) return data.foci_preferred[0];
428
- if (oldestMembership !== undefined) return oldestMembership.getTransport(oldestMembership);
429
- break;
430
- case "multi_sfu":
431
- return data.foci_preferred[0];
432
- default:
433
- // `focus_selection` not understood.
434
- return undefined;
435
- }
436
- break;
433
+ return data.foci_preferred[0];
437
434
  default:
438
435
  return undefined;
439
436
  }
@@ -444,13 +441,6 @@ export class CallMembership {
444
441
  * Or the value of the `foci_preferred` field for legacy session memberships (m.call.member).
445
442
  */
446
443
  public get transports(): Transport[] {
447
- const { kind, data } = this.membershipData;
448
- switch (kind) {
449
- case MembershipKind.RTC:
450
- return data.transports.published;
451
- case MembershipKind.Session:
452
- default:
453
- return data.foci_preferred;
454
- }
444
+ return getTransports(this.membershipData);
455
445
  }
456
446
  }
@@ -1,6 +1,6 @@
1
1
  import { type EncryptionConfig } from "./MatrixRTCSession.ts";
2
2
  import { type CallMembership } from "./CallMembership.ts";
3
- import { type EncryptionKeyMapKey } from "./types.ts";
3
+ import { type Transport, type EncryptionKeyMapKey } from "./types.ts";
4
4
 
5
5
  /**
6
6
  * The string used for the keys in the the encryption key map.
@@ -25,9 +25,11 @@ export interface IEncryptionManager {
25
25
  * Joins the encryption manager with the provided configuration.
26
26
  *
27
27
  * @param joinConfig - The configuration for joining encryption, or undefined
28
- * if no specific configuration is provided.
28
+ * @param publishedTransports - The transports on which we are publishing.
29
+ * (Needed to determine which participant identity formats to use, for
30
+ * compatibility.)
29
31
  */
30
- join(joinConfig: EncryptionConfig | undefined): void;
32
+ join(joinConfig: EncryptionConfig | undefined, publishedTransports: Transport[]): void;
31
33
 
32
34
  /**
33
35
  * Leaves the encryption manager, cleaning up any associated resources.
@@ -52,7 +54,7 @@ export interface IEncryptionManager {
52
54
  key: Uint8Array<ArrayBuffer>;
53
55
  keyIndex: number;
54
56
  membership: CallMembershipIdentityParts;
55
- rtcBackendIdentity: string;
57
+ backendIdentity: string;
56
58
  }>
57
59
  >;
58
60
  }