Skip to content

Commit

Permalink
Add support for MSC4190 (#175)
Browse files Browse the repository at this point in the history
  • Loading branch information
surakin authored Jan 15, 2025
1 parent bba5542 commit 33a034a
Show file tree
Hide file tree
Showing 3 changed files with 42 additions and 11 deletions.
4 changes: 4 additions & 0 deletions mautrix/bridge/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@ def do_update(self, helper: ConfigUpdateHelper) -> None:
copy("bridge.encryption.default")
copy("bridge.encryption.require")
copy("bridge.encryption.appservice")
copy("bridge.encryption.msc4190")
copy("bridge.encryption.delete_keys.delete_outbound_on_ack")
copy("bridge.encryption.delete_keys.dont_store_outbound")
copy("bridge.encryption.delete_keys.ratchet_on_decrypt")
Expand Down Expand Up @@ -241,3 +242,6 @@ def generate_registration(self) -> None:
if self["appservice.ephemeral_events"]:
self._registration["de.sorunome.msc2409.push_ephemeral"] = True
self._registration["push_ephemeral"] = True

if self["bridge.encryption.msc4190"]:
self._registration["io.element.msc4190"] = True
34 changes: 23 additions & 11 deletions mautrix/bridge/e2ee.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ class EncryptionManager:
appservice_mode: bool
periodically_delete_expired_keys: bool
delete_outdated_inbound: bool
msc4190: bool

bridge: br.Bridge
az: AppService
Expand Down Expand Up @@ -108,6 +109,7 @@ def __init__(
self.crypto.send_keys_min_trust = TrustState.parse(verification_levels["receive"])
self.key_sharing_enabled = bridge.config["bridge.encryption.allow_key_sharing"]
self.appservice_mode = bridge.config["bridge.encryption.appservice"]
self.msc4190 = bridge.config["bridge.encryption.msc4190"]
if self.appservice_mode:
self.az.otk_handler = self.crypto.handle_as_otk_counts
self.az.device_list_handler = self.crypto.handle_as_device_lists
Expand Down Expand Up @@ -246,7 +248,7 @@ async def decrypt(self, evt: EncryptedEvent, wait_session_timeout: int = 5) -> M

async def start(self) -> None:
flows = await self.client.get_login_flows()
if not flows.supports_type(LoginType.APPSERVICE):
if not self.msc4190 and not flows.supports_type(LoginType.APPSERVICE):
self.log.critical(
"Encryption enabled in config, but homeserver does not support appservice login"
)
Expand All @@ -261,16 +263,26 @@ async def start(self) -> None:
device_id = await self.crypto_store.get_device_id()
if device_id:
self.log.debug(f"Found device ID in database: {device_id}")
# We set the API token to the AS token here to authenticate the appservice login
# It'll get overridden after the login
self.client.api.token = self.az.as_token
await self.client.login(
login_type=LoginType.APPSERVICE,
device_name=self.device_name,
device_id=device_id,
store_access_token=True,
update_hs_url=False,
)

if self.msc4190:
if not device_id:
self.log.debug("Creating bot device with MSC4190")
self.client.api.token = self.az.as_token
await self.client.create_device_msc4190(
device_id=device_id, initial_display_name=self.device_name
)
else:
# We set the API token to the AS token here to authenticate the appservice login
# It'll get overridden after the login
self.client.api.token = self.az.as_token
await self.client.login(
login_type=LoginType.APPSERVICE,
device_name=self.device_name,
device_id=device_id,
store_access_token=True,
update_hs_url=False,
)

await self.crypto.load()
if not device_id:
await self.crypto_store.put_device_id(self.client.device_id)
Expand Down
15 changes: 15 additions & 0 deletions mautrix/client/api/authentication.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
from __future__ import annotations

import secrets

from mautrix.api import Method, Path
from mautrix.errors import MatrixResponseError
from mautrix.types import (
Expand Down Expand Up @@ -117,6 +119,19 @@ async def login(
self.api.base_url = base_url.rstrip("/")
return resp_data

async def create_device_msc4190(self, device_id: str, initial_display_name: str) -> None:
"""
Create a Device for a user of the homeserver using appservice interface defined in MSC4190
"""
if len(device_id) == 0:
device_id = DeviceID(secrets.token_urlsafe(10))
self.api.as_user_id = self.mxid
await self.api.request(
Method.PUT, Path.v3.devices[device_id], {"display_name": initial_display_name}
)
self.api.as_device_id = device_id
self.device_id = device_id

async def logout(self, clear_access_token: bool = True) -> None:
"""
Invalidates an existing access token, so that it can no longer be used for authorization.
Expand Down

0 comments on commit 33a034a

Please sign in to comment.