Open mkb79 opened 1 year ago
To make the code above working again, the User Agent must be updated to USER_AGENT = "bodyweight-ios-23491000 (iPhone; iOS 17.2.1; Freeletics 23.49.1; com.Freeletics.Freeletics-Lite; de_DE; MESZ; release)"
.
The complete code now is:
import base64
import hashlib
import hmac
from datetime import datetime, timezone
from typing import Dict
import httpx
MAC_KEY = b'2f8562ec236faf401289537f8a8d53921f3aaab8b56533b5ba2c9006c41e48ea316b65212d59b12e3338c090d2a1f19645a11a2be931bf013188f2da47caecec'
MSG_PREFIX = b"e@*GE(eHj(!+XHlUShWpCDxct0}c=4"
LOGIN_URL = "https://api.freeletics.com/user/v2/password/authentication"
LOGOUT_URL = "https://api.freeletics.com/user/v1/auth/logout"
USER_AGENT = "bodyweight-ios-23491000 (iPhone; iOS 17.2.1; Freeletics 23.49.1; com.Freeletics.Freeletics-Lite; de_DE; MESZ; release)"
def timestamp_ms_now() -> int:
return int(datetime.now(tz=timezone.utc).timestamp() * 1000)
class MessageSigner:
def __init__(self) -> None:
self._signer = hmac.new(MAC_KEY, msg=MSG_PREFIX, digestmod=hashlib.sha256)
def sign(self, data: bytes, timestamp: int) -> bytes:
signer = self._signer.copy()
encoded_timestamp = str(timestamp).encode()
encoded_data = base64.b64encode(data)
signer.update(encoded_data)
signer.update(encoded_timestamp)
msg_digest = signer.digest()
return base64.b64encode(msg_digest)
def get_request_headers(self, data: bytes, timestamp: int) -> Dict[str, str]:
signature = self.sign(data, timestamp)
return {
"X-Authorization-Timestamp": str(timestamp),
"X-Authorization": signature.decode()
}
class LoginAuthorizer(httpx.Auth):
"""Login request authenticator for the httpx package."""
requires_request_body = True
signer = MessageSigner()
def auth_flow(self, request: httpx.Request):
timestamp = timestamp_ms_now()
headers = self.signer.get_request_headers(request.content, timestamp)
request.headers.update(headers)
yield request
login_authorizer = LoginAuthorizer()
async def login(username: str, password: str):
async with httpx.AsyncClient(auth=login_authorizer) as client:
body = {"authentication": {"email": username, "password": password}}
headers = {"User-Agent": USER_AGENT}
resp = await client.post(LOGIN_URL, json=body, headers=headers)
if resp.status_code != 201:
raise Exception()
return resp.json()
if __name__ == "__main__":
import asyncio
user = "USERMAIL"
pw = "USERPASS"
login_response = asyncio.run(login(user, pw))
print(login_response)
Login to Freeletics bodyweight app requires a
X-Authorization
andX-Authorization-Timestamp
header. To prevent these, I used the User-Agent from the Nutrion app in the past, which don’t require these.Now I have found out how to create these required headers.