93

I am hoping to make use of the lovoo API, but don't really know how to start. After running Charles proxy and looking at the traffic, I have come to the following conclusion:

First a GET to https://api.lovoo.com/oauth/requestToken? is sent as soon as a user logs on through the app (iPhone):

    GET /oauth/requestToken? HTTP/1.1
Host    api.lovoo.com
User-Agent  LOVOO/612 (iPhone; iOS 10.2; Scale/3.00)
kissapi-app-idfv    1EC7A8E5-DF16-4E14-8EC9-98DD4772F903
tz  Europe/xxx
kissapi-device-model    iPhone 6s Plus
kissapi-app-version 3.17.0
kissapi-new-oauth   1
kissapi-device  iphone
kissapi-app lovoo
wifi    true
kissapi-adv-id  00000000-0000-0000-0000-000000000000
Connection  keep-alive
kissapi-app-id  7F947A460DAFCA88556B2F35A6D78A3E
Authorization   OAuth oauth_callback="oob", oauth_consumer_key="an.email%40gmail.com", oauth_nonce="A32CCA91-FB7A-4AA3-8314-0A9A6E67045E", oauth_signature="Sq8KTg%2FhVIGBaWgWXprPluczOs4%3D", oauth_signature_method="HMAC-SHA1", oauth_timestamp="1487017515", oauth_version="1.0"
Accept-Language en-CH;q=1, de-CH;q=0.9
kissapi-adv-on  false
kissapi-version 1.20.0
kissapi-update-user-hash    6ea2bd15ea41d0dc8c2615589e2d52ec
Accept  */*
kissapi-device-os   10.2
Accept-Encoding gzip, deflate
kissapi-sync-enabled    1

This also gives following token: oauth_token=44d83e8ef50f&oauth_token_secret=37998f6c6ef2e618

This is followed by another GET to https://api.lovoo.com/oauth/accessToken?:

GET /oauth/accessToken? HTTP/1.1
Host    api.lovoo.com
User-Agent  LOVOO/612 (iPhone; iOS 10.2; Scale/3.00)
kissapi-app-idfv    1EC7A8E5-DF16-4E14-8EC9-98DD4772F903
tz  Europe/xxx
kissapi-device-model    iPhone 6s Plus
kissapi-app-version 3.17.0
kissapi-new-oauth   1
kissapi-device  iphone
kissapi-app lovoo
wifi    true
kissapi-adv-id  00000000-0000-0000-0000-000000000000
Connection  keep-alive
kissapi-app-id  7F947A460DAFCA88556B2F35A6D78A3E
Authorization   OAuth oauth_consumer_key="an.email%40gmail.com", oauth_nonce="080328C9-0A53-4971-85E7-65A43F12DC09", oauth_signature="Km0vd8xtHaQmRgkrGLsiljel13o%3D", oauth_signature_method="HMAC-SHA1", oauth_timestamp="1487017515", oauth_token="44d83e8ef50f", oauth_version="1.0"
Accept-Language en-CH;q=1, de-CH;q=0.9
kissapi-adv-on  false
kissapi-version 1.20.0
kissapi-update-user-hash    6ea2bd15ea41d0dc8c2615589e2d52ec
Accept  */*
kissapi-device-os   10.2
Accept-Encoding gzip, deflate
kissapi-sync-enabled    1

And provides the following token: oauth_token=60c8977c8fe9509f&oauth_token_secret=549619c0ef4c4be7d7cb898e

Now, a request to https://api.lovoo.com/init can be made:

GET /init HTTP/1.1
Host    api.lovoo.com
User-Agent  LOVOO/612 (iPhone; iOS 10.2; Scale/3.00)
kissapi-app-idfv    1EC7A8E5-DF16-4E14-8EC9-98DD4772F903
tz  Europe/xxx
kissapi-device-model    iPhone 6s Plus
kissapi-app-version 3.17.0
kissapi-new-oauth   1
kissapi-device  iphone
kissapi-app lovoo
wifi    true
kissapi-adv-id  00000000-0000-0000-0000-000000000000
Connection  keep-alive
kissapi-app-id  7F947A460DAFCA88556B2F35A6D78A3E
Authorization   OAuth oauth_consumer_key="an.email%40gmail.com", oauth_nonce="B622CE9C-DA3D-435C-939A-C58B83DBE85C", oauth_signature="0irvAsilrrdCCdLfu%2F0XSj7THlc%3D", oauth_signature_method="HMAC-SHA1", oauth_timestamp="1487017515", oauth_token="60c8977c8fe9509f", oauth_version="1.0"
Accept-Language en-CH;q=1, de-CH;q=0.9
kissapi-adv-on  false
kissapi-version 1.20.0
kissapi-update-user-hash    6ea2bd15ea41d0dc8c2615589e2d52ec
Accept  */*
kissapi-device-os   10.2
Accept-Encoding gzip, deflate
kissapi-sync-enabled    1

These are the headers I captured, but I don't know how to send them and get the Oauth authentication working, espescially with oauth_nonce.

requests-oauthlib seems to support it, but I don't know which of the token correspond to which variable:

from requests_oauthlib import OAuth1Session

lovoo = OAuth1Session(
    'client_key',
    client_secret='client_secret',
    resource_owner_key='resource_owner_key',
    resource_owner_secret='resource_owner_secret'
)
url = 'https://api.lovoo.com/init'
r = lovoo.get(url)
SuperStormer
  • 3,554
  • 4
  • 16
  • 29
rhillhouse
  • 1,551
  • 1
  • 13
  • 28
  • 4
    Nonce is generated by the client, it's random, you may have to match expected format. You will probably need to supply same nonce in 2 calls. See https://en.wikipedia.org/wiki/Cryptographic_nonce It seems here you have 2 nonce values -- one created by client and another by server. – Dima Tisnek Feb 16 '17 at 14:10
  • What about the OAuth keys/secrets? – rhillhouse Feb 18 '17 at 11:15
  • Have you found out how to get access to the API now? – Kelvin Mar 26 '17 at 19:58
  • @Kelvin unfortunately not. – rhillhouse Mar 27 '17 at 10:37
  • @ou_snaaksie Currently I'm trying to update [this](https://github.com/danieleargento/LoovoAutoLike/blob/1.0/autolike.py) code, since it doesn't work anymore for me, but looks like Lovoo changed their login mechanisms. – Kelvin Mar 27 '17 at 12:18
  • 3
    @Kelvin This post was specifically about accessing the mobile API and emulate an iPhone using it. I have managed to log in to Lovoo using the WebAPI using requests. Make a new question, tag me and I'll help you. I would prefer not posting the whole script, as they might change the login method again. – rhillhouse Mar 28 '17 at 07:00
  • 5
    @rhillhouse, would it be possible for you to write an answer to this question? just to summarize what you found out and your solution – hongsy Jan 20 '20 at 13:05
  • @hongsy I was unable to spoof the iOS client. It was easier going over their homepage. While logging in to the site, view the network traffic with dev tools. The login was/is pretty simple and easy to simulate in python. There are some examples of this on GitHub, such as https://github.com/sarandafl/lovpy/blob/master/lovpy.py – rhillhouse Sep 09 '20 at 03:36

1 Answers1

1

An alternative approach would be the use of selenium for login and further using the session with requests.

E.g.

def login(user, pw):
    chrome_options = Options()
    chrome_options.add_experimental_option("detach", True)
    driver = webdriver.Chrome(options=chrome_options)
    driver.get("https://www.lovoo.com/login_check")
    user_agent = driver.execute_script("return navigator.userAgent;")
    iframe = driver.find_element(By.ID,"gdpr-consent-notice")
    driver.switch_to.frame(iframe)
    privacybutton = WebDriverWait(driver, 10).until(
        EC.presence_of_element_located((By.XPATH,"//b[contains(text(),'Accept All')]"))
    )
    privacybutton.click()
    driver.switch_to.default_content()
    loginbutton = WebDriverWait(driver, 4).until(
        EC.presence_of_element_located((By.XPATH, "//button[contains(text(),'Log in')]"))
    )
    loginbutton.click()
    loginbutton2=WebDriverWait(driver, 4).until(
        EC.presence_of_element_located((By.XPATH, "//button[contains(@data-automation-id,'login-submit-button')]"))
    )
    driver.find_element(By.XPATH, '//input[@name="authEmail"]').send_keys(user)
    driver.find_element(By.XPATH, '//input[@name="authPassword"]').send_keys(pw)
    webdriver.ActionChains(driver).move_to_element(loginbutton2).click(loginbutton2).perform()
    element = WebDriverWait(driver, 10).until(
        EC.presence_of_element_located((By.ID, "topmenu"))
    )
    return user_agent, driver.get_cookies()
(lat,lon)=(0.0,0.0)
with requests.Session() as session:
    user_agent, cookies = login(user, pw)
    session.cookies.update({c['name']: c['value'] for c in cookies})
    session.headers.update({'User-Agent': user_agent})
    session.get(f'https://www.lovoo.com/api_web.php/users?ageFrom=0&ageTo=2&latitude={lat}&longitude={lon}')
sayer
  • 21
  • 1