Skip to content
Draft
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions pkg/connector/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -445,8 +445,9 @@ func (m *MetaClient) connectE2EE() error {
isNew = true
m.WADevice = m.Main.DeviceStore.NewDevice()
}
if suggested := m.Client.MessengerLite.GetSuggestedDeviceID(); suggested != uuid.Nil {
m.WADevice.FacebookUUID = suggested
if known := m.LoginMeta.MessengerLite.DeviceID; known != "" {
// we know this uuid is safe to parse because we generated it
m.WADevice.FacebookUUID = uuid.MustParse(known)
}
m.Client.SetDevice(m.WADevice)

Expand Down
25 changes: 18 additions & 7 deletions pkg/connector/login.go
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,7 @@ func loginWithCookies(
bridgeUser *bridgev2.User,
conn *MetaConnector,
c *cookies.Cookies,
mlm metaid.MessengerLiteLoginMetadata,
) (*bridgev2.LoginStep, error) {

log.Debug().
Expand Down Expand Up @@ -263,9 +264,10 @@ func loginWithCookies(
Name: user.GetName(),
},
Metadata: &metaid.UserLoginMetadata{
Platform: c.Platform,
Cookies: c,
LoginUA: loginUA,
Platform: c.Platform,
Cookies: c,
LoginUA: loginUA,
MessengerLite: mlm,
},
}, nil)
if err != nil {
Expand Down Expand Up @@ -311,7 +313,7 @@ func (m *MetaCookieLogin) SubmitCookies(ctx context.Context, strCookies map[stri
if err != nil {
return nil, err
}
return loginWithCookies(ctx, log, client, m.User, m.Main, c)
return loginWithCookies(ctx, log, client, m.User, m.Main, c, metaid.MessengerLiteLoginMetadata{})
}

type MetaNativeLogin struct {
Expand Down Expand Up @@ -351,7 +353,7 @@ func (m *MetaNativeLogin) Wait(ctx context.Context) (*bridgev2.LoginStep, error)
func (m *MetaNativeLogin) proceed(ctx context.Context, userInput map[string]string) (*bridgev2.LoginStep, error) {
log := m.User.Log.With().Str("component", "messagix").Logger()

step, newCookies, err := m.SavedClient.MessengerLite.DoLoginSteps(ctx, userInput)
step, loginResp, err := m.SavedClient.MessengerLite.DoLoginSteps(ctx, userInput)
if err != nil {
log.Error().Err(err).Msg("Login steps returned error")
if errors.As(err, &bloks.CheckpointError{}) {
Expand All @@ -363,11 +365,20 @@ func (m *MetaNativeLogin) proceed(ctx context.Context, userInput map[string]stri
return step, nil
}

_ = newCookies
newCookies := loginResp.GetCookies()

m.SavedClient.GetCookies().UpdateValues(newCookies.GetAll())

step, err = loginWithCookies(ctx, log, m.SavedClient, m.User, m.Main, newCookies)
loginMeta := m.SavedClient.MessengerLite.GetLoginMetadata()
step, err = loginWithCookies(
ctx, log, m.SavedClient, m.User, m.Main, newCookies,
metaid.MessengerLiteLoginMetadata{
DeviceID: loginMeta.DeviceID,
FamilyDeviceID: loginMeta.FamilyDeviceID,
MachineID: loginMeta.MachineID,
AccessToken: loginResp.AccessToken,
},
)
if err != nil {
return nil, err
}
Expand Down
72 changes: 43 additions & 29 deletions pkg/connector/push.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import (
"go.mau.fi/mautrix-meta/pkg/messagix"
"go.mau.fi/mautrix-meta/pkg/messagix/methods"
"go.mau.fi/mautrix-meta/pkg/messagix/table"
"go.mau.fi/mautrix-meta/pkg/messagix/types"
"go.mau.fi/mautrix-meta/pkg/metaid"
)

Expand All @@ -40,12 +41,21 @@ var (
_ bridgev2.BackgroundSyncingNetworkAPI = (*MetaClient)(nil)
)

var pushCfg = &bridgev2.PushConfig{
var webPushCfg = &bridgev2.PushConfig{
Web: &bridgev2.WebPushConfig{VapidKey: "BIBn3E_rWTci8Xn6P9Xj3btShT85Wdtne0LtwNUyRQ5XjFNkuTq9j4MPAVLvAFhXrUU1A9UxyxBA7YIOjqDIDHI"},
}

var apnsPushCfg = &bridgev2.PushConfig{
APNs: &bridgev2.APNsPushConfig{
BundleID: "com.facebook.Messenger",
},
}

func (m *MetaClient) GetPushConfigs() *bridgev2.PushConfig {
return pushCfg
if m.Client.Platform == types.MessengerLite {
return apnsPushCfg
}
return webPushCfg
}

type DoubleToken struct {
Expand All @@ -54,9 +64,6 @@ type DoubleToken struct {
}

func (m *MetaClient) RegisterPushNotifications(ctx context.Context, pushType bridgev2.PushType, token string) error {
if pushType != bridgev2.PushTypeWeb {
return fmt.Errorf("unsupported push type %s", pushType)
}
meta := m.UserLogin.Metadata.(*metaid.UserLoginMetadata)
if meta.PushKeys == nil {
meta.GeneratePushKeys()
Expand All @@ -69,32 +76,39 @@ func (m *MetaClient) RegisterPushNotifications(ctx context.Context, pushType bri
P256DH: meta.PushKeys.P256DH,
Auth: meta.PushKeys.Auth,
}
var encToken string
if token[0] == '{' && token[len(token)-1] == '}' {
var dt DoubleToken
err := json.Unmarshal([]byte(token), &dt)
if err != nil {
return fmt.Errorf("failed to unmarshal double token: %w", err)
switch pushType {
case bridgev2.PushTypeWeb:
var encToken string
if token[0] == '{' && token[len(token)-1] == '}' {
var dt DoubleToken
err := json.Unmarshal([]byte(token), &dt)
if err != nil {
return fmt.Errorf("failed to unmarshal double token: %w", err)
}
token = dt.Unencrypted
encToken = dt.Encrypted
}
token = dt.Unencrypted
encToken = dt.Encrypted
}
if encToken != "" {
err := m.E2EEClient.RegisterForPushNotifications(ctx, &whatsmeow.WebPushConfig{
Endpoint: encToken,
Auth: meta.PushKeys.Auth,
P256DH: meta.PushKeys.P256DH,
})
if err != nil {
return fmt.Errorf("failed to register e2ee notifications: %w", err)
if encToken != "" {
err := m.E2EEClient.RegisterForPushNotifications(ctx, &whatsmeow.WebPushConfig{
Endpoint: encToken,
Auth: meta.PushKeys.Auth,
P256DH: meta.PushKeys.P256DH,
})
if err != nil {
return fmt.Errorf("failed to register e2ee notifications: %w", err)
}
}
}
if cli := m.Client; cli == nil {
return messagix.ErrClientIsNil
} else if cli.Platform.IsMessenger() {
return cli.Facebook.RegisterPushNotifications(ctx, token, keys)
} else {
return cli.Instagram.RegisterPushNotifications(ctx, token, keys)
if cli := m.Client; cli == nil {
return messagix.ErrClientIsNil
} else if cli.Platform.IsMessenger() {
return cli.Facebook.RegisterPushNotifications(ctx, token, keys)
} else {
return cli.Instagram.RegisterPushNotifications(ctx, token, keys)
}
case bridgev2.PushTypeAPNs:
return m.Client.MessengerLite.RegisterPushNotifications(ctx, token, keys, meta.MessengerLite)
default:
return fmt.Errorf("unsupported push type %s", pushType)
}
}

Expand Down
2 changes: 1 addition & 1 deletion pkg/messagix/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -291,7 +291,7 @@ func (c *Client) buildHeaders(withCookies, isSecFetchDocument bool) http.Header

func (c *Client) buildMessengerLiteHeaders() (http.Header, error) {

analHdr, err := makeRequestAnalyticsHeader()
analHdr, err := makeRequestAnalyticsHeader(true)
if err != nil {
return nil, err
}
Expand Down
128 changes: 107 additions & 21 deletions pkg/messagix/messengerlite.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
"net/http"
"net/url"
"strings"
"time"

"github.com/google/uuid"
"maunium.net/go/mautrix/bridgev2"
Expand All @@ -16,23 +17,28 @@
"go.mau.fi/mautrix-meta/pkg/messagix/crypto"
"go.mau.fi/mautrix-meta/pkg/messagix/types"
"go.mau.fi/mautrix-meta/pkg/messagix/useragent"
"go.mau.fi/mautrix-meta/pkg/metaid"
)

type MessengerLiteLoginState struct {
DeviceID string
FamilyDeviceID string
MachineID string
}

type MessengerLiteMethods struct {
client *Client

browser *bloks.Browser

deviceID uuid.UUID
familyDeviceID uuid.UUID
machineID string
loginState MessengerLiteLoginState
}

func (fb *MessengerLiteMethods) GetSuggestedDeviceID() uuid.UUID {
func (fb *MessengerLiteMethods) GetLoginMetadata() MessengerLiteLoginState {
if fb == nil {
return uuid.Nil
return MessengerLiteLoginState{}
}
return fb.deviceID
return fb.loginState
}

type NetworkTags struct {
Expand All @@ -46,15 +52,17 @@
NetworkTags NetworkTags `json:"network_tags"`
}

func makeRequestAnalyticsHeader() (string, error) {
func makeRequestAnalyticsHeader(includeCategory bool) (string, error) {
anal := RequestAnalytics{
NetworkTags: NetworkTags{
Product: useragent.MessengerLiteAppId,
RequestCategory: "graphql",
Purpose: "fetch",
RetryAttempt: "0",
Product: useragent.MessengerLiteAppId,
RetryAttempt: "0",
},
}
if includeCategory {
anal.NetworkTags.RequestCategory = "graphql"
anal.NetworkTags.Purpose = "fetch"
}
hdr, err := json.Marshal(anal)
if err != nil {
return "", fmt.Errorf("make analytics header: %w", err)
Expand Down Expand Up @@ -88,10 +96,12 @@
func (c *Client) fetchLightspeedKey(ctx context.Context) (*LightspeedKeyResponse, error) {
endpoint := c.GetEndpoint("pwd_key")

loginMeta := c.MessengerLite.GetLoginMetadata()

params := map[string]any{
"access_token": useragent.MessengerLiteAccessToken,
"device_id": strings.ToUpper(c.MessengerLite.deviceID.String()),
"machine_id": c.MessengerLite.machineID,
"device_id": loginMeta.DeviceID,
"machine_id": loginMeta.MachineID,
"version": "3",
}

Expand All @@ -101,7 +111,7 @@
}
fullURL := endpoint + "?" + query.Encode()

analHdr, err := makeRequestAnalyticsHeader()
analHdr, err := makeRequestAnalyticsHeader(true)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -172,7 +182,7 @@
SessionCookies []RawCookie `json:"session_cookies"`
}

func convertCookies(payload *BloksLoginActionResponsePayload) *cookies.Cookies {
func (payload *BloksLoginActionResponsePayload) GetCookies() *cookies.Cookies {
newCookies := &cookies.Cookies{Platform: types.MessengerLite}
newCookies.UpdateValues(make(map[cookies.MetaCookieName]string))
for _, raw := range payload.SessionCookies {
Expand All @@ -181,14 +191,15 @@
return newCookies
}

func (m *MessengerLiteMethods) DoLoginSteps(ctx context.Context, userInput map[string]string) (*bridgev2.LoginStep, *cookies.Cookies, error) {
func (m *MessengerLiteMethods) DoLoginSteps(ctx context.Context, userInput map[string]string) (*bridgev2.LoginStep, *BloksLoginActionResponsePayload, error) {
if m.browser == nil {
m.browser = bloks.NewBrowser(m.getBrowserConfig())

// these values are generated by us, they are known safe
m.deviceID = uuid.MustParse(m.browser.Bridge.DeviceID)
m.familyDeviceID = uuid.MustParse(m.browser.Bridge.FamilyDeviceID)
m.machineID = m.browser.Bridge.MachineID
m.loginState = MessengerLiteLoginState{
DeviceID: m.browser.Bridge.DeviceID,
FamilyDeviceID: m.browser.Bridge.FamilyDeviceID,
MachineID: m.browser.Bridge.MachineID,
}
}

for m.browser.State != bloks.StateSuccess {
Expand All @@ -214,5 +225,80 @@
return nil, nil, fmt.Errorf("parsing login response data: %w", err)
}

return nil, convertCookies(&loginRespPayload), nil
return nil, &loginRespPayload, nil
}

type apnsEncryptionParams struct {
Algorithm string `json:"algorithm"`
Key []byte `json:"key"`
KeyID string `json:"key_id"`
}

type apnsExtraData struct {
IPhoneSettingMask int `json:"iphone_setting_mask"`
}

type apnsProtocolParams struct {
DeviceID string `json:"device_id"`
Encryption apnsEncryptionParams `json:"encryption"`
ExtraData apnsExtraData `json:"extra_data"`
FamilyDeviceID string `json:"family_device_id"`
IOSIdentifierForVendor string `json:"ios_identifier_for_vendor"`
IsNonPhone bool `json:"is_non_phone"`
Token string `json:"token"`
URL string `json:"url"`
}

func (m *MessengerLiteMethods) RegisterPushNotifications(ctx context.Context, endpoint string, keys PushKeys, loginMeta metaid.MessengerLiteLoginMetadata) error {
protocol := apnsProtocolParams{
DeviceID: loginMeta.DeviceID,
Encryption: apnsEncryptionParams{
Algorithm: "AES_GCM",
Key: []byte(keys.P256DH),
KeyID: fmt.Sprintf("%s", time.Now().Unix()),

Check failure on line 258 in pkg/messagix/messengerlite.go

View workflow job for this annotation

GitHub Actions / Lint (old)

Printf format %s has arg #1 of wrong type int64 (SA5009)

Check failure on line 258 in pkg/messagix/messengerlite.go

View workflow job for this annotation

GitHub Actions / Lint (old)

fmt.Sprintf format %s has arg time.Now().Unix() of wrong type int64

Check failure on line 258 in pkg/messagix/messengerlite.go

View workflow job for this annotation

GitHub Actions / Lint (latest)

Printf format %s has arg #1 of wrong type int64 (SA5009)

Check failure on line 258 in pkg/messagix/messengerlite.go

View workflow job for this annotation

GitHub Actions / Lint (latest)

fmt.Sprintf format %s has arg time.Now().Unix() of wrong type int64

Check failure on line 258 in pkg/messagix/messengerlite.go

View workflow job for this annotation

GitHub Actions / Lint (latest)

Printf format %s has arg #1 of wrong type int64 (SA5009)

Check failure on line 258 in pkg/messagix/messengerlite.go

View workflow job for this annotation

GitHub Actions / Lint (latest)

fmt.Sprintf format %s has arg time.Now().Unix() of wrong type int64

Check failure on line 258 in pkg/messagix/messengerlite.go

View workflow job for this annotation

GitHub Actions / Lint (old)

Printf format %s has arg #1 of wrong type int64 (SA5009)

Check failure on line 258 in pkg/messagix/messengerlite.go

View workflow job for this annotation

GitHub Actions / Lint (old)

fmt.Sprintf format %s has arg time.Now().Unix() of wrong type int64
},
ExtraData: apnsExtraData{
IPhoneSettingMask: 640,
},
FamilyDeviceID: loginMeta.FamilyDeviceID,
IOSIdentifierForVendor: strings.ToUpper(uuid.New().String()),
IsNonPhone: false,
Token: endpoint,
URL: "http://push.apple.com/pushkit/voip",
}

protocolB, err := json.Marshal(protocol)
if err != nil {
return fmt.Errorf("marshal apns protocl params: %w", err)
}

form := url.Values{}
form.Add("access_token", loginMeta.AccessToken)
form.Add("protocol_params", string(protocolB))

analHdr, err := makeRequestAnalyticsHeader(false)
if err != nil {
return err
}
headers := map[string]string{
"accept": "*/*",
"x-fb-http-engine": "NSURL",
"x-fb-request-analytics-tags": analHdr,
"request_token": strings.ToUpper(uuid.New().String()),
"accept-language": "en-US,en;q=0.9",
"user-agent": useragent.MessengerLiteUserAgent,
"x-fb-appid": useragent.MessengerLiteAppId,
}

httpHeaders := http.Header{}
for k, v := range headers {
httpHeaders.Set(k, v)
}

resp, _, err := m.client.MakeRequest(ctx, "https://graph.facebook.com/v2.10/me/register_push_tokens", "POST", httpHeaders, []byte(form.Encode()), types.FORM)

Check failure on line 298 in pkg/messagix/messengerlite.go

View workflow job for this annotation

GitHub Actions / Lint (old)

this value of err is never used (SA4006)

Check failure on line 298 in pkg/messagix/messengerlite.go

View workflow job for this annotation

GitHub Actions / Lint (latest)

this value of err is never used (SA4006)

Check failure on line 298 in pkg/messagix/messengerlite.go

View workflow job for this annotation

GitHub Actions / Lint (latest)

this value of err is never used (SA4006)

Check failure on line 298 in pkg/messagix/messengerlite.go

View workflow job for this annotation

GitHub Actions / Lint (old)

this value of err is never used (SA4006)
if resp.StatusCode != http.StatusOK {
return fmt.Errorf("bad http status on push registration: %d", resp.StatusCode)
}

return nil
}
Loading
Loading