After days of struggles I've finally found a solution! It's definitely not nice and is using Firebase internal API which isn't really available externally. My solution uses two sessions.
First one is an authorized session from google.auth.transport.requests.AuthorizedSession
, credentials are generated as in the article: https://developers.google.com/docs/api/quickstart/python and then passed to AuthorizedSession
. This session is working with documented firebase REST API, for example to fetch all released versions of the app.
The second one is used for internal Firebase API and requires cookies from browser session. In order to get the cookies I had to login to firebase and navigate to app distribution to download the app. I opened dev console (F12) and observed "Network" tab. Firebase is making 2 requests to download the app. First one is to get the download URL and the second one is just calling the URL returned by the previous request. First of two requests has the address like https://firebaseappdistribution-pa.clients6.google.com/v1/{app_name}:getLatestBinary?alt=json&key={api_key}
. app_name
is from authenticated session when getting all app releases. api_key
can be copied from the browser URL or from the first request parameters. Sending a GET request with these parameters won't work, it also requires cookies and headers.
Sufficient headers are:
{
'origin': 'https://console.firebase.google.com',
'referer': 'https://console.firebase.google.com/',
'x-goog-authuser': '<has to be copied from the first request from browser>'
}
When it comes to the cookies, things are slightly more complicated, the request requires following cookies: "APISID", "HSID", "SAPISID", "SID" and "SSID". I copied them from the first request. Then all that is missing is one additional cookie: "SAPISIDHASH", thanks to this answer, I've created my implementation in python:
def calculate_sapisid_hash(cookies):
"""Calculates SAPISIDHASH based on cookies. Required in authorization to download apk from firebase"""
epoch = int(time())
sha = sha1(' '.join([str(int(epoch)), cookies['SAPISID'], 'https://console.firebase.google.com']).encode())
return f'SAPISIDHASH {int(epoch)}_{sha.hexdigest()}'
Finally by adding "SAPISIDHASH" to the cookies, we can send GET request on the first URL and get a valid response with "fileURL" and "fileSize" in it. Having "fileURL" we can save the final file with requests.get
/ wget
/ curl
as there is no additional authorization needed