Skip to content

Commit

Permalink
Add support for MSC4190
Browse files Browse the repository at this point in the history
  • Loading branch information
onestacked committed Sep 20, 2024
1 parent a95101e commit 7a82015
Show file tree
Hide file tree
Showing 6 changed files with 65 additions and 14 deletions.
1 change: 1 addition & 0 deletions appservice/registration.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ type Registration struct {
SoruEphemeralEvents bool `yaml:"de.sorunome.msc2409.push_ephemeral,omitempty" json:"de.sorunome.msc2409.push_ephemeral,omitempty"`
EphemeralEvents bool `yaml:"push_ephemeral,omitempty" json:"push_ephemeral,omitempty"`
MSC3202 bool `yaml:"org.matrix.msc3202,omitempty" json:"org.matrix.msc3202,omitempty"`
MSC4190 bool `yaml:"io.element.msc4190,omitempty" json:"io.element.msc4190,omitempty"`
}

// CreateRegistration creates a Registration with random appservice and homeserver tokens.
Expand Down
12 changes: 11 additions & 1 deletion bridge/crypto.go
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,16 @@ func (helper *CryptoHelper) loginBot(ctx context.Context) (*mautrix.Client, bool
} else if !flows.HasFlow(mautrix.AuthTypeAppservice) {
return nil, deviceID != "", fmt.Errorf("homeserver does not support appservice login")
}
initialDeviceDisplayName := fmt.Sprintf("%s bridge", helper.bridge.ProtocolName)
if helper.bridge.AS.Registration.MSC4190 {
err = client.CreateDeviceMSC4190(ctx, deviceID, initialDeviceDisplayName)
if err != nil {
return nil, deviceID != "", fmt.Errorf("failed to log in as bridge bot: %w", err)
}
helper.store.DeviceID = client.DeviceID
return client, deviceID != "", nil
}

resp, err := client.Login(ctx, &mautrix.ReqLogin{
Type: mautrix.AuthTypeAppservice,
Identifier: mautrix.UserIdentifier{
Expand All @@ -241,7 +251,7 @@ func (helper *CryptoHelper) loginBot(ctx context.Context) (*mautrix.Client, bool
DeviceID: deviceID,
StoreCredentials: true,

InitialDeviceDisplayName: fmt.Sprintf("%s bridge", helper.bridge.ProtocolName),
InitialDeviceDisplayName: initialDeviceDisplayName,
})
if err != nil {
return nil, deviceID != "", fmt.Errorf("failed to log in as bridge bot: %w", err)
Expand Down
18 changes: 13 additions & 5 deletions bridgev2/matrix/crypto.go
Original file line number Diff line number Diff line change
Expand Up @@ -247,17 +247,25 @@ func (helper *CryptoHelper) loginBot(ctx context.Context) (*mautrix.Client, bool
} else if !flows.HasFlow(mautrix.AuthTypeAppservice) {
return nil, deviceID != "", fmt.Errorf("homeserver does not support appservice login")
}
initialDeviceDisplayName := fmt.Sprintf("%s bridge", helper.bridge.Bridge.Network.GetName().DisplayName)
if helper.bridge.AS.Registration.MSC4190 {
err = client.CreateDeviceMSC4190(ctx, deviceID, initialDeviceDisplayName)
if err != nil {
return nil, deviceID != "", fmt.Errorf("failed to log in as bridge bot: %w", err)
}
helper.store.DeviceID = client.DeviceID
return client, deviceID != "", nil
}

resp, err := client.Login(ctx, &mautrix.ReqLogin{
Type: mautrix.AuthTypeAppservice,
Identifier: mautrix.UserIdentifier{
Type: mautrix.IdentifierTypeUser,
User: string(helper.bridge.AS.BotMXID()),
},
DeviceID: deviceID,
StoreCredentials: true,

// TODO find proper bridge name
InitialDeviceDisplayName: "Megabridge", // fmt.Sprintf("%s bridge", helper.bridge.ProtocolName),
DeviceID: deviceID,
StoreCredentials: true,
InitialDeviceDisplayName: initialDeviceDisplayName,
})
if err != nil {
return nil, deviceID != "", fmt.Errorf("failed to log in as bridge bot: %w", err)
Expand Down
22 changes: 22 additions & 0 deletions client.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (

"github.com/rs/zerolog"
"go.mau.fi/util/ptr"
"go.mau.fi/util/random"
"go.mau.fi/util/retryafter"
"golang.org/x/exp/maps"

Expand Down Expand Up @@ -897,6 +898,27 @@ func (cli *Client) Login(ctx context.Context, req *ReqLogin) (resp *RespLogin, e
return
}

// Create a Device for a user of the homeserver using appservice interface defined in MSC4190
func (cli *Client) CreateDeviceMSC4190(ctx context.Context, deviceID id.DeviceID, initialDispalyName string) (err error) {
if len(deviceID) == 0 {
deviceID = id.DeviceID(random.String(10))
}
if !cli.SetAppServiceUserID {
return fmt.Errorf("CreateDeviceMSC4190 requires SetAppServiceUserID to be enabled")
}
if cli.AccessToken == "" {
return fmt.Errorf("CreateDeviceMSC4190 requires The AS AccessToken token to be set as the client AccessToken")
}
_, err = cli.MakeRequest(ctx, http.MethodPost, cli.BuildClientURL("v3", "devices", deviceID), ReqPutDevice{
DisplayName: initialDispalyName,
}, nil)
if err != nil {
return err
}
cli.DeviceID = deviceID
return nil
}

// Logout the current user. See https://spec.matrix.org/v1.2/client-server-api/#post_matrixclientv3logout
// This does not clear the credentials from the client instance. See ClearCredentials() instead.
func (cli *Client) Logout(ctx context.Context) (resp *RespLogout, err error) {
Expand Down
23 changes: 15 additions & 8 deletions crypto/cryptohelper/cryptohelper.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ type CryptoHelper struct {
DecryptErrorCallback func(*event.Event, error)

LoginAs *mautrix.ReqLogin
MSC4190 bool

ASEventProcessor crypto.ASEventProcessor
CustomPostDecrypt func(context.Context, *event.Event)
Expand Down Expand Up @@ -152,7 +153,20 @@ func (helper *CryptoHelper) Init(ctx context.Context) error {
return fmt.Errorf("failed to find existing device ID: %w", err)
}
if helper.LoginAs != nil && helper.LoginAs.Type == mautrix.AuthTypeAppservice && helper.client.SetAppServiceDeviceID {
if storedDeviceID == "" {
if storedDeviceID != "" {
helper.log.Debug().
Str("username", helper.LoginAs.Identifier.User).
Stringer("device_id", storedDeviceID).
Msg("Using existing device")
rawCryptoStore.DeviceID = storedDeviceID
helper.client.DeviceID = storedDeviceID
} else if helper.MSC4190 {
helper.log.Debug().
Str("username", helper.LoginAs.Identifier.User).
Msg("Creating device with MSC4190")
helper.client.CreateDeviceMSC4190(ctx, storedDeviceID, "")
rawCryptoStore.DeviceID = helper.client.DeviceID
} else {
helper.log.Debug().
Str("username", helper.LoginAs.Identifier.User).
Msg("Logging in with appservice")
Expand All @@ -163,13 +177,6 @@ func (helper *CryptoHelper) Init(ctx context.Context) error {
}
rawCryptoStore.DeviceID = resp.DeviceID
helper.client.DeviceID = resp.DeviceID
} else {
helper.log.Debug().
Str("username", helper.LoginAs.Identifier.User).
Stringer("device_id", storedDeviceID).
Msg("Using existing device")
rawCryptoStore.DeviceID = storedDeviceID
helper.client.DeviceID = storedDeviceID
}
} else if helper.LoginAs != nil {
if storedDeviceID != "" {
Expand Down
3 changes: 3 additions & 0 deletions requests.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,9 @@ type ReqLogin struct {
// Whether or not the returned .well-known data should update the homeserver URL in the Client
StoreHomeserverURL bool `json:"-"`
}
type ReqPutDevice struct {
DisplayName string `json:"display_name,omitempty"`
}

type ReqUIAuthFallback struct {
Session string `json:"session"`
Expand Down

0 comments on commit 7a82015

Please sign in to comment.