Pagina 1 van 6

Toegang krijgen tot de OMNI API

Geplaatst: 05 mar 2018, 10:31
door joost
[Discussie over het verkrijgen van toegang tot de OMNI API afgesplitst naar een eigen topic. Stromeur.]

Dat stromer geen API vrijgeeft betekent opzich niet dat er niks mee te doen is. Met behulp van een stukje software (mitmproxy) kan je het verkeer tussen de app en stromer service afluisteren en dus de functionaliteiten die de app bevat zelf nuttig gebruiken. Als ik begin April mijn Stromer krijg heb ik in ieder geval een leuk projectje.

Re: Nieuwe Stromer OMNI technologie 2018: OMNI Connect

Geplaatst: 05 mar 2018, 10:46
door Stromeur
joost schreef:
05 mar 2018, 10:31
een leuk projectje.
Zeker! Open daar tegen die tijd gerust een topic over als je zin hebt om je ervaringen hiermee te delen.

Re: Nieuwe Stromer OMNI technologie 2018: OMNI Connect

Geplaatst: 08 jul 2018, 22:26
door joost
Vandaag eens in de api van Stromer gedoken. Via deze url loopt de verbinding https://api3.stromer-portal.ch/rapi/mobile/v2/bike/ krijg alleen de authenticatie nog niet goed buiten de app.

Waar ik al wel uit ben is dat er geen actuele gps data via de api te vinden is. De omni stuurt dus niet zo vaak een gps update naar de cloud.

ik kwam wel data tegen die niet in de app terug te vinden is zoals:

"atmospheric_pressure": 10370,
"average_energy_consumption": 109,
"power_on_cycles": 34,

Nummers van onderdelen. Naast de display, controller en motor ook een serie nr van de fork.

Al met al niet heel interessant om verder naar te kijken. De beveiliging ziet er wel goed uit dus wat dat betreft ben ik tevreden :ay

Re: Nieuwe Stromer OMNI technologie 2018: OMNI Connect

Geplaatst: 08 jul 2018, 22:29
door Stromeur
Ik kan echt niet wat jij kunt, dus ik ben diep onder de indruk. Ik zou die URL nog niet eens gevonden hebben. Hoe kom ik bijvoorbeeld in de API van mijn eigen Stromer?

Mocht je nog meer van dit soort boeiende informatie tegen komen, shoot!

Re: Nieuwe Stromer OMNI technologie 2018: OMNI Connect

Geplaatst: 08 jul 2018, 22:30
door FreddyH
joost schreef:
08 jul 2018, 22:26
Vandaag eens in de api van Stromer gedoken. Via deze url loopt de verbinding https://api3.stromer-portal.ch/rapi/mobile/v2/bike/ krijg alleen de authenticatie nog niet goed buiten de app.
Kan je met een netwerk-sniffer een session id of zo achterhalen?

Re: Nieuwe Stromer OMNI technologie 2018: OMNI Connect

Geplaatst: 09 jul 2018, 19:49
door joost
@FreddyH ik heb mitmproxy gebruikt. Mijn iPhone verbind direct met deze proxy en dan zie ik al het verkeer dat over de lijn gaat.

Op deze pagina kom je in de app https://api3.stromer-portal.ch/users/login/

Naast je credentials heb je ook nog een client_id nodig (die heb ik) alleen na de submit wordt je naar de app geredirect.

https://api3.stromer-portal.ch/users/lo ... nt_id=JEID

redirect_uri al wel aangepast naar api3.stromer-portal.ch maar dat gaat niet goed. Wellicht dat iemand de android apk eens kan uitpakken en zien wat daar gebeurd.

Re: Toegang krijgen tot de OMNI API

Geplaatst: 13 jul 2018, 19:44
door FreddyH
Je bent alweer een stapje verder. Ik zou graag ook een keer gaan hacken, maar heb zelf geen Stromer dus dat gaat hem niet worden... Wellicht weer een keer een paar dagen een 'proefrit' maken :dwarf:

Die Android APK uitpakken kun je zelf natuurlijk ook. Moet ik deze downloaden en naar je toe sturen?

Re: Toegang krijgen tot de OMNI API

Geplaatst: 04 okt 2018, 11:05
door merstro
Ik heb het inmiddels even uitgezocht met een beetje python code:

Code: Selecteer alles

import requests
from IPython.display import HTML
from urllib.parse import urlencode, parse_qs, splitquery

password = 'xxx'
username = 'xxx'
client_id = 'xxxxxxx'
client_secret = 'xxxxxxxxxxxxx'

def get_code(client_id, username, password):
    url = "https://api3.stromer-portal.ch/users/login/"
    s = requests.session()
    res = s.get(url)
    HTML(res.text)
    s.cookies

    qs = urlencode(
        {
            "client_id": client_id,
            "response_type": "code",
            "redirect_url": "stromerauth://auth",
            "scope": "bikeposition bikestatus bikeconfiguration bikelock biketheft bikedata bikepin bikeblink userprofile",
        }
    )

    data = {
        "password": password,
        "username": username,
        "csrfmiddlewaretoken": s.cookies.get("csrftoken"),
        "next": "/o/authorize/?" + qs,
    }

    res = s.post(url, data=data, headers=dict(Referer=url), allow_redirects=False)
    res = s.send(res.next, allow_redirects=False)
    _, qs = splitquery(res.headers["Location"])
    code = parse_qs(qs)["code"][0]
    return code

def get_access_token(client_id, client_secret, code):
    url = "https://api3.stromer-portal.ch//o/token/"
    params = {
        "grant_type": "authorization_code",
        "client_id": client_id,
        "client_secret": client_secret,
        "code": code,
        "redirect_uri": "stromerauth://auth",
    }

    res = requests.post(url, params=params)
    return res.json()["access_token"]
    
    
def call_api(access_token, endpoint, params={}):
    url = f"https://api3.stromer-portal.ch/rapi/mobile/v2/{endpoint}"
    headers = {"Authorization": f"Bearer {access_token}"}
    res = requests.get(url, headers=headers, params={})
    return res.json()["data"][0]

Code: Selecteer alles

import json
endpoint="bike/"
bike = call_api(access_token, endpoint)
print('bike:', json.dumps(bike, indent=True))


bike = call_api(access_token, endpoint="bike/")
endpoint = f'bike/{bike["bikeid"]}/state/'
params = {'cached':'false'}
state = call_api(access_token, endpoint, params)
print('state:', json.dumps(state, indent=True))

bike: {
 "bikeid": ***,
 "biketype": "ST2",
 "color": "silver",
 "hardware": "omniinterface",
 "bikemodel": "ST2",
 "nickname": "***",
 "size": "sport 20"
}
state: {
 "battery_SOC": 40,
 "suiversion": "3.4.2.1",
 "bike_speed": 0.0,
 "tntversion": "3.5",
 "trip_time": 81401,
 "light_on": 255,
 "average_speed_trip": 34.9,
 "trip_distance": 788.5,
 "average_speed_total": 34.1,
 "motor_temp": 16.0,
 "average_energy_consumption": 141,
 "power_on_cycles": 670,
 "total_time": 318409,
 "atmospheric_pressure": 0,
 "battery_temp": 24.0,
 "battery_health": 95,
 "total_distance": 3013.6,
 "assistance_level": 0,
 "rcvts": 1538643543,
 "theft_flag": false,
 "lock_flag": true,
 "total_energy_consumption": 42594
}

Ik weet niet of ik die client_id en client_secret zo mag posten, maar zijn zijn heel gemakkelijk uit de APK te halen (of door een MITM op de app). Ik ga dit gebruiken om een tooltje te maken dat verifieert dat de fiets goed wordt opgeladen. Ik heb heel soms dat ie na een uurtje ofzo stopt met opladen en dan kom ik bijna niet meer thuis. Straks checkt ie dat gewoon ieder kwartier: staat de fiets bij mijn werk en is de batterij niet aan het opladen ==> stuur een mailtje

Re: Toegang krijgen tot de OMNI API

Geplaatst: 04 okt 2018, 11:52
door JeroenDG
Knap gedaan :ay
Met die informatie ontstaan er best wat leuke mogelijkheden.

Re: Toegang krijgen tot de OMNI API

Geplaatst: 05 okt 2018, 15:33
door TimB
merstro schreef:
04 okt 2018, 11:05
Ik heb het inmiddels even uitgezocht met een beetje python code:
Met dank aan @merstro heb ik een portje naar NodeJS gemaakt:

Code: Selecteer alles

const request = require("request-promise");
const qs = require("qs");
const cheerio = require("cheerio");

class Stromer {
  constructor(options) {
    this.client_id = options.client_id;
    this.client_secret = options.client_secret;
    this.username = options.username;
    this.password = options.password;
    this.jar = request.jar();

    this.LOGIN_URL = "https://api3.stromer-portal.ch/users/login/";
    this.TOKEN_URL = "https://api3.stromer-portal.ch/o/token/";
    this.API_URL = "https://api3.stromer-portal.ch/rapi/mobile/v2/";
  }

  async getCode() {
    const loginPage = await request({
      url: this.LOGIN_URL,
      jar: this.jar
    });
    const cookies = this.jar.getCookieString(this.LOGIN_URL);
    const [_, csrf] = cookies.split("=");

    const queryString = qs.stringify({
      client_id: this.client_id,
      response_type: "code",
      redirect_url: "stromerauth://auth",
      scope:
        "bikeposition bikestatus bikeconfiguration bikelock biketheft bikedata bikepin bikeblink userprofile"
    });

    const $ = cheerio.load(loginPage);

    const formCsrf = $("input[name=csrfmiddlewaretoken]").val();

    const form = {
      username: this.username,
      password: this.password,
      csrfmiddlewaretoken: formCsrf,
      next: `/o/authorize/?${queryString}`
    };

    let code = "";
    try {
      const redirect = await request({
        url: this.LOGIN_URL,
        method: "post",
        form,
        jar: this.jar,
        followRedirect: false,
        headers: {
          Referer: this.LOGIN_URL
        }
      });
    } catch (e) {
      try {
        const authorize = await request({
          url: "https://api3.stromer-portal.ch" + e.response.headers.location,
          followRedirect: false,
          jar: this.jar
        });
      } catch (e) {
        const location = e.response.headers.location;
        const [_, code] = location.split("=");
        return code;
      }
    }
  }

  async getAccessToken(code) {
    const data = {
      grant_type: "authorization_code",
      client_id: this.client_id,
      client_secret: this.client_secret,
      code,
      redirect_uri: "stromerauth://auth"
    };
    const json = await request({
      url: this.TOKEN_URL,
      method: "post",
      formData: data,
      jar: this.jar
    });
    return JSON.parse(json).access_token;
  }

  async call(endpoint) {
    if (this.accessToken) {
      return await this._callApi(this.accessToken, endpoint);
    } else {
      const code = await this.getCode();
      this.accessToken = await this.getAccessToken(code);
      if (this.accessToken) {
        return await this._callApi(this.accessToken, endpoint);
      } else {
        throw new Error("No access token could be retrieved");
      }
    }
  }

  async _callApi(access_token, endpoint) {
    const response = await request({
      url: `${this.API_URL}${endpoint}`,
      headers: { Authorization: `Bearer ${access_token}` }
    });

    return JSON.parse(response).data;
  }
}
Gebruik:

Code: Selecteer alles

const test = new Stromer({
  username: "xxxxx",
  password: "xxxxxx",
  client_id: "xxxxxxx",
  client_secret: "xxxxxxx"
});

test.call("bike").then(c => console.log(c));