Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
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
1 change: 1 addition & 0 deletions pkg/messagix/endpoints/facebook.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ func makeMessengerLiteEndpoints(host string) map[string]string {
endpoints := makeFacebookEndpoints(host)
endpoints["graph_graphql"] = "https://graph.facebook.com/graphql"
endpoints["pwd_key"] = "https://graph.facebook.com/pwd_key_fetch"
endpoints["get_session_for_app"] = "https://api.facebook.com/method/auth.getSessionForApp"
endpoints["v2.10"] = "https://graph.facebook.com/v2.10"
endpoints["cat"] = "https://web.facebook.com/messaging/lightspeed/cat"
endpoints["icdc_fetch"] = "https://v.whatsapp.net/v2/fb_icdc_fetch"
Expand Down
79 changes: 70 additions & 9 deletions pkg/messagix/messengerlite.go
Original file line number Diff line number Diff line change
Expand Up @@ -150,14 +150,72 @@ type BloksLoginActionResponsePayload struct {
}

func convertCookies(payload *BloksLoginActionResponsePayload) *cookies.Cookies {
return cookiesFromRaw(payload.SessionCookies)
}

func cookiesFromRaw(rawCookies []RawCookie) *cookies.Cookies {
newCookies := &cookies.Cookies{Platform: types.MessengerLite}
newCookies.UpdateValues(make(map[cookies.MetaCookieName]string))
for _, raw := range payload.SessionCookies {
for _, raw := range rawCookies {
newCookies.Set(cookies.MetaCookieName(raw.Name), raw.Value)
}
return newCookies
}

type getSessionForAppResponse struct {
SessionCookies []RawCookie `json:"session_cookies"`
UID int64 `json:"uid"`

// Present when the request fails.
ErrorCode int `json:"error_code"`
ErrorMsg string `json:"error_msg"`
ErrorSubcode int `json:"error_subcode"`
}

func (c *Client) ExchangeTransientToken(ctx context.Context, transientToken string) (*cookies.Cookies, error) {
endpoint := c.GetEndpoint("get_session_for_app")

query := url.Values{}
query.Set("access_token", transientToken)
query.Set("new_app_id", useragent.MessengerLiteAppID)
query.Set("generate_session_cookies", "1")
query.Set("format", "json")
fullURL := endpoint + "?" + query.Encode()

analyticsTags, err := httpclient.MakeRequestAnalyticsHeader()
if err != nil {
return nil, err
}
httpHeaders := http.Header{}
for k, v := range map[string]string{
"accept": "*/*",
"x-fb-appid": useragent.MessengerLiteAppID,
"x-fb-request-analytics-tags": analyticsTags,
"user-agent": useragent.MessengerLiteUserAgent,
"accept-language": "en-US,en;q=0.9",
"request_token": uuid.New().String(),
} {
httpHeaders.Set(k, v)
}

_, responseBytes, err := c.http.MakeRequest(ctx, fullURL, "GET", httpHeaders, nil, types.NONE)
if err != nil {
return nil, fmt.Errorf("requesting session for transient token: %w", err)
}

var response getSessionForAppResponse
if err = json.Unmarshal(responseBytes, &response); err != nil {
return nil, fmt.Errorf("parsing session-for-app response: %w", err)
}
if response.ErrorCode != 0 {
return nil, fmt.Errorf("session-for-app error %d/%d: %s", response.ErrorCode, response.ErrorSubcode, response.ErrorMsg)
}
if len(response.SessionCookies) == 0 {
return nil, fmt.Errorf("session-for-app returned no cookies for transient token")
}
return cookiesFromRaw(response.SessionCookies), nil
}

// For testing only
func (m *MessengerLiteMethods) SetDeviceIdentifiers(deviceID uuid.UUID) {
m.deviceID = deviceID
Expand Down Expand Up @@ -196,14 +254,17 @@ func (m *MessengerLiteMethods) DoLoginSteps(ctx context.Context, userInput map[s
return nil, nil, fmt.Errorf("parsing login response data: %w", err)
}

if loginRespPayload.CredentialType == "transient_token" {
// There is an extra step for getting cookies when the credential_type
// field is transient_token, which the bridge does not currently
// implement. Until then, add a warning log. Normally the credential_type
// is two_factor. It depends on the account, not on the MFA method
// selected.
m.client.Logger.Warn().Msg("Got credential_type transient_token, login will fail")
return nil, nil, ErrTransientTokenLogin
if loginRespPayload.CredentialType == "transient_token" && len(loginRespPayload.SessionCookies) == 0 {
if loginRespPayload.AccessToken == "" {
return nil, nil, ErrTransientTokenLogin
}
m.client.Logger.Debug().Msg("Got credential_type transient_token, exchanging for session cookies")
newCookies, err := m.client.ExchangeTransientToken(ctx, loginRespPayload.AccessToken)
if err != nil {
m.client.Logger.Warn().Err(err).Msg("Failed to exchange transient token for session cookies")
return nil, nil, fmt.Errorf("%w: %w", ErrTransientTokenLogin, err)
}
return nil, newCookies, nil
}

if len(loginRespPayload.SessionCookies) == 0 {
Expand Down
Loading