Skip to content

Commit 7a784a4

Browse files
committed
client,bridgev2: Add support for MSC4190
1 parent a95101e commit 7a784a4

File tree

8 files changed

+51
-5
lines changed

8 files changed

+51
-5
lines changed

appservice/registration.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ type Registration struct {
2929
SoruEphemeralEvents bool `yaml:"de.sorunome.msc2409.push_ephemeral,omitempty" json:"de.sorunome.msc2409.push_ephemeral,omitempty"`
3030
EphemeralEvents bool `yaml:"push_ephemeral,omitempty" json:"push_ephemeral,omitempty"`
3131
MSC3202 bool `yaml:"org.matrix.msc3202,omitempty" json:"org.matrix.msc3202,omitempty"`
32+
MSC4190 bool `yaml:"io.element.msc4190,omitempty" json:"io.element.msc4190,omitempty"`
3233
}
3334

3435
// CreateRegistration creates a Registration with random appservice and homeserver tokens.

bridgev2/bridgeconfig/appservice.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ type AppserviceConfig struct {
3434

3535
EphemeralEvents bool `yaml:"ephemeral_events"`
3636
AsyncTransactions bool `yaml:"async_transactions"`
37+
MSC4190 bool `yaml:"msc4190"`
3738

3839
UsernameTemplate string `yaml:"username_template"`
3940
usernameTemplate *template.Template `yaml:"-"`
@@ -77,6 +78,7 @@ func (asc *AppserviceConfig) copyToRegistration(registration *appservice.Registr
7778
registration.RateLimited = &falseVal
7879
registration.EphemeralEvents = asc.EphemeralEvents
7980
registration.SoruEphemeralEvents = asc.EphemeralEvents
81+
registration.MSC4190 = asc.MSC4190
8082
}
8183

8284
// GenerateRegistration generates a registration file for the homeserver.

bridgev2/bridgeconfig/legacymigrate.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ func doMigrateLegacy(helper up.Helper, python bool) {
6363
}
6464
helper.Copy(up.Bool, "appservice", "ephemeral_events")
6565
helper.Copy(up.Bool, "appservice", "async_transactions")
66+
helper.Copy(up.Bool, "appservice", "msc4190")
6667
helper.Copy(up.Str, "appservice", "as_token")
6768
helper.Copy(up.Str, "appservice", "hs_token")
6869

bridgev2/bridgeconfig/upgrade.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ func doUpgrade(helper up.Helper) {
7878
helper.Copy(up.Str, "appservice", "bot", "avatar")
7979
helper.Copy(up.Bool, "appservice", "ephemeral_events")
8080
helper.Copy(up.Bool, "appservice", "async_transactions")
81+
helper.Copy(up.Bool, "appservice", "msc4190")
8182
helper.Copy(up.Str, "appservice", "as_token")
8283
helper.Copy(up.Str, "appservice", "hs_token")
8384
helper.Copy(up.Str, "appservice", "username_template")

bridgev2/matrix/crypto.go

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -241,23 +241,34 @@ func (helper *CryptoHelper) loginBot(ctx context.Context) (*mautrix.Client, bool
241241
// Create a new client instance with the default AS settings (including as_token),
242242
// the Login call will then override the access token in the client.
243243
client := helper.bridge.AS.NewMautrixClient(helper.bridge.AS.BotMXID())
244+
245+
initialDeviceDisplayName := fmt.Sprintf("%s bridge", helper.bridge.Bridge.Network.GetName().DisplayName)
246+
if helper.bridge.Config.AppService.MSC4190 {
247+
helper.log.Debug().Msg("Creating bot device with msc4190")
248+
err = client.CreateDeviceMSC4190(ctx, deviceID, initialDeviceDisplayName)
249+
if err != nil {
250+
return nil, deviceID != "", fmt.Errorf("failed to create device for bridge bot: %w", err)
251+
}
252+
helper.store.DeviceID = client.DeviceID
253+
return client, deviceID != "", nil
254+
}
255+
244256
flows, err := client.GetLoginFlows(ctx)
245257
if err != nil {
246258
return nil, deviceID != "", fmt.Errorf("failed to get supported login flows: %w", err)
247259
} else if !flows.HasFlow(mautrix.AuthTypeAppservice) {
248260
return nil, deviceID != "", fmt.Errorf("homeserver does not support appservice login")
249261
}
262+
250263
resp, err := client.Login(ctx, &mautrix.ReqLogin{
251264
Type: mautrix.AuthTypeAppservice,
252265
Identifier: mautrix.UserIdentifier{
253266
Type: mautrix.IdentifierTypeUser,
254267
User: string(helper.bridge.AS.BotMXID()),
255268
},
256-
DeviceID: deviceID,
257-
StoreCredentials: true,
258-
259-
// TODO find proper bridge name
260-
InitialDeviceDisplayName: "Megabridge", // fmt.Sprintf("%s bridge", helper.bridge.ProtocolName),
269+
DeviceID: deviceID,
270+
StoreCredentials: true,
271+
InitialDeviceDisplayName: initialDeviceDisplayName,
261272
})
262273
if err != nil {
263274
return nil, deviceID != "", fmt.Errorf("failed to log in as bridge bot: %w", err)

bridgev2/matrix/mxmain/example-config.yaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,10 @@ appservice:
186186
# However, messages will not be guaranteed to be bridged in the same order they were sent in.
187187
# This value doesn't affect the registration file.
188188
async_transactions: false
189+
# Wether to use MSC4190 to create DeviceIDs for ghost users.
190+
# Requires the homeserver to support MSC4190 and the device masquerading parts of MSC3202.
191+
# Required when using encryption on the bridge and OIDC Auth (MSC3861) on the homeserver.
192+
msc4190: false
189193

190194
# Authentication tokens for AS <-> HS communication. Autogenerated; do not modify.
191195
as_token: "This value is generated when generating the registration"

client.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import (
2121

2222
"github.com/rs/zerolog"
2323
"go.mau.fi/util/ptr"
24+
"go.mau.fi/util/random"
2425
"go.mau.fi/util/retryafter"
2526
"golang.org/x/exp/maps"
2627

@@ -897,6 +898,28 @@ func (cli *Client) Login(ctx context.Context, req *ReqLogin) (resp *RespLogin, e
897898
return
898899
}
899900

901+
// Create a Device for a user of the homeserver using appservice interface defined in MSC4190
902+
func (cli *Client) CreateDeviceMSC4190(ctx context.Context, deviceID id.DeviceID, initialDispalyName string) (err error) {
903+
if len(deviceID) == 0 {
904+
deviceID = id.DeviceID(random.String(10))
905+
}
906+
if !cli.SetAppServiceUserID {
907+
return fmt.Errorf("CreateDeviceMSC4190 requires SetAppServiceUserID to be enabled")
908+
}
909+
if cli.AccessToken == "" {
910+
return fmt.Errorf("CreateDeviceMSC4190 requires The AS AccessToken token to be set as the client AccessToken")
911+
}
912+
_, err = cli.MakeRequest(ctx, http.MethodPut, cli.BuildClientURL("v3", "devices", deviceID), ReqPutDevice{
913+
DisplayName: initialDispalyName,
914+
}, nil)
915+
if err != nil {
916+
return err
917+
}
918+
cli.DeviceID = deviceID
919+
cli.SetAppServiceDeviceID = true
920+
return nil
921+
}
922+
900923
// Logout the current user. See https://spec.matrix.org/v1.2/client-server-api/#post_matrixclientv3logout
901924
// This does not clear the credentials from the client instance. See ClearCredentials() instead.
902925
func (cli *Client) Logout(ctx context.Context) (resp *RespLogout, err error) {

requests.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,9 @@ type ReqLogin struct {
9090
// Whether or not the returned .well-known data should update the homeserver URL in the Client
9191
StoreHomeserverURL bool `json:"-"`
9292
}
93+
type ReqPutDevice struct {
94+
DisplayName string `json:"display_name,omitempty"`
95+
}
9396

9497
type ReqUIAuthFallback struct {
9598
Session string `json:"session"`

0 commit comments

Comments
 (0)