I'm using a Python requests library for a Flask app, which is hosted on Heroku. I implemented a task using threading which will fetch the data from the other app indefinitely until the user stops it by themselves. In theory, each user has their own access token, and refresh token used for the api call, but somehow, sometimes the api request is sent to wrong account. For example, if the user A and B both run the app, then the user A got an email saying they got an item X, but when the user A checks on the other app, it doesn't show item X, but the user B got that item X instead. What could possibly be wrong here? I suspect the way I use the requests, but I can't think of any reason why. The confusing part is it uses correct user setting to fetch the items, and only "try" to get the item that satisfy the settings, but the Item is just sent to the wrong account.
def process_search():
user = request.user
user.update_user_running_status(True)
user.set_running_session()
user.refresh()
# Custom class I made to take the user info, which has token information for the other app
itemHandler = Handler(user)
thread = Thread(target=itemHandler.start)
thread.daemon = True
thread.start()
running_status = user.running_status
current_running_session = user.current_running_session
return jsonify({'is_running': running_status, "current_running_session": current_running_session}), 200
class Handler:
allHeaders = {
"TokenRequest": {
"x-acme-identity-auth-domain": "api.acme.com",
"User-Agent": "Acme Device Acme/0.0/iOS/15.2/iPhone",
},
"AllRequest": {
"Accept": "application/json",
"x-acme-access-token": None,
"X-Acme-Date": None,
"Accept-Encoding": "gzip, deflate, br",
"Accept-Language": "en-US",
"Content-Type": "application/json",
"User-Agent": "iOS/16.1 (iPhone Darwin) Model/iPhone Platform/iPhone14,2 AcmeOS/2.112.2",
"Connection": "keep-alive",
},
}
def __init__(self, user: User) -> None:
self.user = user
self.requestHeaders = Handler.allHeaders.get("AllRequest")
self.refreshToken = user.acme_refresh_token
self.accessToken = user.acme_access_token
if self.refreshToken == "":
self.registerAccount()
self.requestHeaders["x-acme-access-token"] = self.accessToken
self.requestHeaders["X-Acme-Date"] = self.getDate()
def registerAccount(self):
auth_response = self.user.auth_response
parsed_query = parse_qs(urlparse(auth_response).query)
reg_access_token = unquote(parsed_query['openid.oa2.access_token'][0])
device_id = secrets.token_hex(16)
acme_reg_data = {
"auth_data": {"access_token": reg_access_token},
"cookies": {"domain": ".acme.com", "website_cookies": []},
"device_metadata": {
"android_id": "52aee8aecab31ee3",
"device_os_family": "android",
"device_serial": device_id,
"device_type": "A1MPSLFC7L5AFK",
"mac_address": secrets.token_hex(64).upper(),
"manufacturer": MANUFACTURER,
"model": DEVICE_NAME,
"os_version": "30",
"product": DEVICE_NAME,
},
"registration_data": {
"app_name": APP_NAME,
"app_version": APP_VERSION,
"device_model": DEVICE_NAME,
"device_serial": device_id,
"device_type": "A1MPSLFC7L5AFK",
"domain": "Device",
"os_version": OS_VERSION,
"software_version": "130050002",
},
"requested_extensions": ["device_info", "customer_info"],
"requested_token_type": [
"bearer",
"mac_dms",
"store_authentication_cookie",
"website_cookies",
],
"user_context_map": {"frc": self.generate_frc(device_id)},
}
reg_headers = {
"Content-Type": "application/json",
"Accept-Charset": "utf-8",
"x-acme-identity-auth-domain": "api.acme.com",
"Connection": "keep-alive",
"Accept": "*/*",
"Accept-Language": "en-US",
}
res = requests.post(
Handler.routes.get("GetAuthToken"),
json=acme_reg_data,
headers=reg_headers,
verify=True,
)
if res.status_code != 200:
print("login failed")
exit(1)
res = res.json()
tokens = res['response']['success']['tokens']['bearer']
self.accessToken = tokens['access_token']
self.refreshToken = tokens['refresh_token']
self.user.update_acme_token(self.accessToken, self.refreshToken)
def start(self):
while True:
if not self.user.running_status:
Offer(self.user).delete_temp_offers()
break
# fetch data
response = requests.post(
self.routes.get("getItems"),
headers=self.requestHeaders,
json={
"apiVersion": "V2",
}
)
I suspected the requests.Session, so I change it to regular requests, but the issue still occurs.
This is a classic Python bug, just hidden in a clever way.
When you do this:
self.requestHeaders = Handler.allHeaders.get("AllRequest")
You're not making a COPY of that dictionary. Every object that does that will get a reference to that SAME dictionary. When you modify the dictionary to set the current user's token, you are modifying them all.
If you change this to:
self.requestHeaders = Handler.allHeaders["AllRequest"].copy()
Or, better yet, just initialize the dictionary in the __init__
, then your problems should go away.