magnific0 / nokia-weight-sync

Get weight from Nokia Health and update in Garmin Connect or Smashrun
GNU General Public License v3.0
71 stars 30 forks source link

UPDATED Nokia Health Mate API to OAuth 2.0. Reconfiguration required! #5

Open jschram opened 6 years ago

jschram commented 6 years ago

Nokia sent out a mailing that they'll remove support for oAuth 1.0 as of November 30th, 2018. https://developer.health.nokia.com/oauth2/#tag/OAuth-1.0-(deprecated)

Would it be possible to change the authentication part of the sync to support oAuth 2.0, so the sync will continue to function after that date?

magnific0 commented 6 years ago

@jschram thanks for opening the issue. I have spent the day updating nokia.py to OAuth 2.0. It seems to work great for me.

Updating It is important for everyone that wants to update to OAuth 2.0 to

  1. register a new application at https://account.health.nokia.com/partner/add_oauth2
    1. logo: the requirements are quite strict, feel free to use this one
    2. callback: you can pick anything, but if you want to do the automated authorization (you will be prompted for this), you need to pick the hostname/ip and port carefully. I use http://localhost:8087.
      • localhost: I run nokia-weight-sync and do the authorization in the browser on the same device. This needs to be replaced by local or public ip/hostname if you run it on a server.
      • 8087: a port that is free and not coincidentally used by other services. For a remote setup make sure the port is not firewalled.
      • http: https is available, but requires additional setup of certificates. For localhost http is fine.
  2. redo the configuration with nokia-weight-sync setup nokia.

I'll leave this issue open for visibility and users to add their findings to.

ltgustavsen commented 6 years ago

I have deleted the nokia section in my previous working config file. I wonder if I now misunderstand what I should type in the callback url?

nokia-weight-sync.git/trunk$ ./nokia-weight-sync.py setup nokia
To set a connection with Nokia Health you must have registered an application at https://account.health.nokia.com/partner/add_oauth2 .
Please enter the client id: 784********************************************
Please enter the consumer secret: ***********************************************
Please enter the callback uri known by Nokia: https://localhost/
https://account.health.nokia.com/oauth2_user/authorize2?response_type=code&client_id=***************************************************&redirect_uri=https%3A%2F%2Flocalhost%2F&scope=user.metrics&state=************************
Go to https://account.health.nokia.com/oauth2_user/authorize2?response_type=code&client_id=**********************************&redirect_uri=https%3A%2F%2Flocalhost%2F&scope=user.metrics&state=**************************** allow the app and authorize the application.
Please enter your callback url: https://localhost/
Traceback (most recent call last):
  File "./nokia-weight-sync.py", line 199, in <module>
    setup_nokia( options, config )
  File "./nokia-weight-sync.py", line 83, in setup_nokia
    creds = auth.get_credentials(callback_url)
  File "/home/ltg/Dokumenter/nokia-weight-sync.git/trunk/nokia.py", line 84, in get_credentials
    client_secret=self.consumer_secret)
  File "/home/ltg/.local/lib/python3.6/site-packages/requests_oauthlib/oauth2_session.py", line 187, in fetch_token
    state=self._state)
  File "/home/ltg/.local/lib/python3.6/site-packages/oauthlib/oauth2/rfc6749/clients/web_application.py", line 174, in parse_request_uri_response
    response = parse_authorization_code_response(uri, state=state)
  File "/home/ltg/.local/lib/python3.6/site-packages/oauthlib/oauth2/rfc6749/parameters.py", line 227, in parse_authorization_code_response
    raise MissingCodeError("Missing code parameter in response.")
oauthlib.oauth2.rfc6749.errors.MissingCodeError: (missing_code) Missing code parameter in response.
magnific0 commented 6 years ago

Hi @ltgustavsen you should input the url you were redirected to after you authorized the application. It is your callback url with some additional parameters: https://localhost/?code=[code]&state=[state]

I have just made some clarifications to the setup section for Nokia to make this a bit clearer.

ltgustavsen commented 6 years ago

thumbs up

ltgustavsen commented 6 years ago

Well it worked fine yesterday. Today I first received this error:

python3 nokia-weight-sync.py sync garmin
Traceback (most recent call last):
  File "nokia-weight-sync.py", line 274, in <module>
    groups = client_nokia.get_measures(lastupdate=last_sync)
  File "/home/ltg/Dokumenter/nokia-weight-sync.git/trunk/nokia.py", line 164, in get_measures
    r = self.request('measure', 'getmeas', kwargs)
  File "/home/ltg/Dokumenter/nokia-weight-sync.git/trunk/nokia.py", line 152, in request
    raise Exception("Error code %s" % response['status'])
Exception: Error code 401

The I tried now a few hours later:

 python3 nokia-weight-sync.py sync garmin
Traceback (most recent call last):
  File "/home/ltg/.local/lib/python3.6/site-packages/requests_oauthlib/oauth2_session.py", line 330, in request
    http_method=method, body=data, headers=headers)
  File "/home/ltg/.local/lib/python3.6/site-packages/oauthlib/oauth2/rfc6749/clients/base.py", line 196, in add_token
    raise TokenExpiredError()
oauthlib.oauth2.rfc6749.errors.TokenExpiredError: (token_expired) 

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "nokia-weight-sync.py", line 274, in <module>
    groups = client_nokia.get_measures(lastupdate=last_sync)
  File "/home/ltg/Dokumenter/nokia-weight-sync.git/trunk/nokia.py", line 164, in get_measures
    r = self.request('measure', 'getmeas', kwargs)
  File "/home/ltg/Dokumenter/nokia-weight-sync.git/trunk/nokia.py", line 149, in request
    r = self.client.request(method, '/'.join(url_parts), params=params)
  File "/home/ltg/.local/lib/python3.6/site-packages/requests_oauthlib/oauth2_session.py", line 343, in request
    self.auto_refresh_url, auth=auth, **kwargs
  File "/home/ltg/.local/lib/python3.6/site-packages/requests_oauthlib/oauth2_session.py", line 309, in refresh_token
    self.token = self._client.parse_request_body_response(r.text, scope=self.scope)
  File "/home/ltg/.local/lib/python3.6/site-packages/oauthlib/oauth2/rfc6749/clients/base.py", line 411, in parse_request_body_response
    self.token = parse_token_response(body, scope=scope)
  File "/home/ltg/.local/lib/python3.6/site-packages/oauthlib/oauth2/rfc6749/parameters.py", line 379, in parse_token_response
    validate_token_parameters(params)
  File "/home/ltg/.local/lib/python3.6/site-packages/oauthlib/oauth2/rfc6749/parameters.py", line 389, in validate_token_parameters
    raise MissingTokenError(description="Missing access token parameter.")
oauthlib.oauth2.rfc6749.errors.MissingTokenError: (missing_token) Missing access token parameter.
magnific0 commented 6 years ago

Hmm I'll have a look at it later why you got the 401.

The last message is a result of the access and refresh tokens both expired. The new refresh token wasn't saved because of the error you saw earlier. The only solution is to re setup Nokia. You don't need to remove the section this time.

jschram commented 6 years ago

Thanks for the change! It seems to work when I run it right after the first sync. I have it scheduled to run daily, will report back if it ran correctly on the next iteration.

ltgustavsen commented 6 years ago

I have now resetup every day: I even deleted the nokia config yesterday. But after a few hours my access toke expires. I have also changed localhost to a public webservice I have. Here is a test this evening. And a screenshot just showing that many metrics are exported. I have a Nokia body+. 63169

pi@raspberrypi:~/nokia-weight-sync $ date;python3 nokia-weight-sync.py  sync garmin
Tue 14 Aug 14:04:06 CEST 2018
Traceback (most recent call last):
  File "/home/pi/.local/lib/python3.5/site-packages/requests_oauthlib/oauth2_session.py", line 330, in request
    http_method=method, body=data, headers=headers)
  File "/home/pi/.local/lib/python3.5/site-packages/oauthlib/oauth2/rfc6749/clients/base.py", line 196, in add_token
    raise TokenExpiredError()
oauthlib.oauth2.rfc6749.errors.TokenExpiredError: (token_expired) 

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "nokia-weight-sync.py", line 274, in <module>
    groups = client_nokia.get_measures(lastupdate=last_sync)
  File "/home/pi/nokia-weight-sync/nokia.py", line 164, in get_measures
    r = self.request('measure', 'getmeas', kwargs)
  File "/home/pi/nokia-weight-sync/nokia.py", line 149, in request
    r = self.client.request(method, '/'.join(url_parts), params=params)
  File "/home/pi/.local/lib/python3.5/site-packages/requests_oauthlib/oauth2_session.py", line 343, in request
    self.auto_refresh_url, auth=auth, **kwargs
  File "/home/pi/.local/lib/python3.5/site-packages/requests_oauthlib/oauth2_session.py", line 309, in refresh_token
    self.token = self._client.parse_request_body_response(r.text, scope=self.scope)
  File "/home/pi/.local/lib/python3.5/site-packages/oauthlib/oauth2/rfc6749/clients/base.py", line 411, in parse_request_body_response
    self.token = parse_token_response(body, scope=scope)
  File "/home/pi/.local/lib/python3.5/site-packages/oauthlib/oauth2/rfc6749/parameters.py", line 379, in parse_token_response
    validate_token_parameters(params)
  File "/home/pi/.local/lib/python3.5/site-packages/oauthlib/oauth2/rfc6749/parameters.py", line 389, in validate_token_parameters
    raise MissingTokenError(description="Missing access token parameter.")
oauthlib.oauth2.rfc6749.errors.MissingTokenError: (missing_token) Missing access token parameter.

pi@raspberrypi:~/nokia-weight-sync $ 
pi@raspberrypi:~/nokia-weight-sync $ 
pi@raspberrypi:~/nokia-weight-sync $ 
pi@raspberrypi:~/nokia-weight-sync $ date; ./nokia-weight-sync.py -k xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx -s xxxxxxxxxxxxxxxxxxxxxxxxx setup nokia
Tue 14 Aug 14:04:54 CEST 2018
Please enter the callback url known by Nokia: https://xxxxx.duckdns.org/states/
Visit: https://account.health.nokia.com/oauth2_user/authorize2?response_type=code&client_id=xxxxxxxxxxxxxxxxxxxxxxxxx&redirect_uri=https%3A%2F%2Fxxxxx.duckdns.org%2Fstates%2F&scope=user.metrics&state=Lu6Evhxxxxxxxxxx
and select your user and click "Allow this app".
Afterwards you will be redirected to your callback url with some additional parameters.
Example: https://your_original_callback?code=[code]&state=[state]
Please enter the full callback response url: https://xxxxxx.duckdns.org/states/?code=xxxxxxxxx&state=xxxxxxxx
Config file saved to config.ini
pi@raspberrypi:~/nokia-weight-sync $ 
pi@raspberrypi:~/nokia-weight-sync $ 
pi@raspberrypi:~/nokia-weight-sync $ 
pi@raspberrypi:~/nokia-weight-sync $ date;python3 nokia-weight-sync.py  sync garmin
Tue 14 Aug 14:06:06 CEST 2018
Garmin Connect User Name: xxxxxxx
2 weights has been successfully updated to Garmin!
Config file saved to config.ini
Exception ignored in: <function WeakValueDictionary.__init__.<locals>.remove at 0x75f2bc90>
Traceback (most recent call last):
  File "/usr/lib/python3.5/weakref.py", line 117, in remove
TypeError: 'NoneType' object is not callable
Exception ignored in: <function WeakValueDictionary.__init__.<locals>.remove at 0x75f2bc90>
Traceback (most recent call last):
  File "/usr/lib/python3.5/weakref.py", line 117, in remove
TypeError: 'NoneType' object is not callable
pi@raspberrypi:~/nokia-weight-sync $ 
pi@raspberrypi:~/nokia-weight-sync $ 
pi@raspberrypi:~/nokia-weight-sync $ date;python3 nokia-weight-sync.py  sync garmin
Tue 14 Aug 14:08:01 CEST 2018
Last measurement was already synced
pi@raspberrypi:~/nokia-weight-sync $ 
pi@raspberrypi:~/nokia-weight-sync $ 
pi@raspberrypi:~/nokia-weight-sync $ date;python3 nokia-weight-sync.py  sync garmin
Tue 14 Aug 14:19:54 CEST 2018
Last measurement was already synced
pi@raspberrypi:~/nokia-weight-sync $ 
pi@raspberrypi:~/nokia-weight-sync $ date;python3 nokia-weight-sync.py  sync garmin
Tue 14 Aug 15:25:11 CEST 2018
Last measurement was already synced
pi@raspberrypi:~/nokia-weight-sync $ 
pi@raspberrypi:~/nokia-weight-sync $ 
pi@raspberrypi:~/nokia-weight-sync $ 
pi@raspberrypi:~/nokia-weight-sync $ date;python3 nokia-weight-sync.py  sync garmin
Tue 14 Aug 15:55:00 CEST 2018
Last measurement was already synced
pi@raspberrypi:~/nokia-weight-sync $ 
pi@raspberrypi:~/nokia-weight-sync $ 
pi@raspberrypi:~/nokia-weight-sync $ 
pi@raspberrypi:~/nokia-weight-sync $ date;python3 nokia-weight-sync.py  sync garmin
Tue 14 Aug 16:51:10 CEST 2018
Last measurement was already synced
pi@raspberrypi:~/nokia-weight-sync $ 
pi@raspberrypi:~/nokia-weight-sync $ 
pi@raspberrypi:~/nokia-weight-sync $ date;python3 nokia-weight-sync.py  sync garmin
Tue 14 Aug 17:59:40 CEST 2018
Traceback (most recent call last):
  File "nokia-weight-sync.py", line 274, in <module>
    groups = client_nokia.get_measures(lastupdate=last_sync)
  File "/home/pi/nokia-weight-sync/nokia.py", line 164, in get_measures
    r = self.request('measure', 'getmeas', kwargs)
  File "/home/pi/nokia-weight-sync/nokia.py", line 152, in request
    raise Exception("Error code %s" % response['status'])
Exception: Error code 401
pi@raspberrypi:~/nokia-weight-sync $ 
pi@raspberrypi:~/nokia-weight-sync $ 
pi@raspberrypi:~/nokia-weight-sync $ 
pi@raspberrypi:~/nokia-weight-sync $ date;python3 nokia-weight-sync.py  sync garmin
Tue 14 Aug 18:00:17 CEST 2018
Traceback (most recent call last):
  File "nokia-weight-sync.py", line 274, in <module>
    groups = client_nokia.get_measures(lastupdate=last_sync)
  File "/home/pi/nokia-weight-sync/nokia.py", line 164, in get_measures
    r = self.request('measure', 'getmeas', kwargs)
  File "/home/pi/nokia-weight-sync/nokia.py", line 152, in request
    raise Exception("Error code %s" % response['status'])
Exception: Error code 401
pi@raspberrypi:~/nokia-weight-sync $ 
pi@raspberrypi:~/nokia-weight-sync $ 
pi@raspberrypi:~/nokia-weight-sync $ 
pi@raspberrypi:~/nokia-weight-sync $ date;python3 nokia-weight-sync.py  sync garmin
Tue 14 Aug 18:41:15 CEST 2018
Traceback (most recent call last):
  File "nokia-weight-sync.py", line 274, in <module>
    groups = client_nokia.get_measures(lastupdate=last_sync)
  File "/home/pi/nokia-weight-sync/nokia.py", line 164, in get_measures
    r = self.request('measure', 'getmeas', kwargs)
  File "/home/pi/nokia-weight-sync/nokia.py", line 152, in request
    raise Exception("Error code %s" % response['status'])
Exception: Error code 401
pi@raspberrypi:~/nokia-weight-sync $ 
pi@raspberrypi:~/nokia-weight-sync $ 
pi@raspberrypi:~/nokia-weight-sync $ 
pi@raspberrypi:~/nokia-weight-sync $ 
pi@raspberrypi:~/nokia-weight-sync $ date;python3 nokia-weight-sync.py  sync garmin
Tue 14 Aug 20:58:43 CEST 2018
Traceback (most recent call last):
  File "nokia-weight-sync.py", line 274, in <module>
    groups = client_nokia.get_measures(lastupdate=last_sync)
  File "/home/pi/nokia-weight-sync/nokia.py", line 164, in get_measures
    r = self.request('measure', 'getmeas', kwargs)
  File "/home/pi/nokia-weight-sync/nokia.py", line 152, in request
    raise Exception("Error code %s" % response['status'])
Exception: Error code 401
pi@raspberrypi:~/nokia-weight-sync $ 
pi@raspberrypi:~/nokia-weight-sync $ 
pi@raspberrypi:~/nokia-weight-sync $ 
pi@raspberrypi:~/nokia-weight-sync $ date;python3 nokia-weight-sync.py  sync garmin
Tue 14 Aug 21:37:30 CEST 2018
Traceback (most recent call last):
  File "nokia-weight-sync.py", line 274, in <module>
    groups = client_nokia.get_measures(lastupdate=last_sync)
  File "/home/pi/nokia-weight-sync/nokia.py", line 164, in get_measures
    r = self.request('measure', 'getmeas', kwargs)
  File "/home/pi/nokia-weight-sync/nokia.py", line 152, in request
    raise Exception("Error code %s" % response['status'])
Exception: Error code 401
pi@raspberrypi:~/nokia-weight-sync $ 
pi@raspberrypi:~/nokia-weight-sync $ 
pi@raspberrypi:~/nokia-weight-sync $ 
pi@raspberrypi:~/nokia-weight-sync $ 
pi@raspberrypi:~/nokia-weight-sync $ date;python3 nokia-weight-sync.py  sync garmin
Tue 14 Aug 22:15:48 CEST 2018
Traceback (most recent call last):
  File "/home/pi/.local/lib/python3.5/site-packages/requests_oauthlib/oauth2_session.py", line 330, in request
    http_method=method, body=data, headers=headers)
  File "/home/pi/.local/lib/python3.5/site-packages/oauthlib/oauth2/rfc6749/clients/base.py", line 196, in add_token
    raise TokenExpiredError()
oauthlib.oauth2.rfc6749.errors.TokenExpiredError: (token_expired) 

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "nokia-weight-sync.py", line 274, in <module>
    groups = client_nokia.get_measures(lastupdate=last_sync)
  File "/home/pi/nokia-weight-sync/nokia.py", line 164, in get_measures
    r = self.request('measure', 'getmeas', kwargs)
  File "/home/pi/nokia-weight-sync/nokia.py", line 149, in request
    r = self.client.request(method, '/'.join(url_parts), params=params)
  File "/home/pi/.local/lib/python3.5/site-packages/requests_oauthlib/oauth2_session.py", line 343, in request
    self.auto_refresh_url, auth=auth, **kwargs
  File "/home/pi/.local/lib/python3.5/site-packages/requests_oauthlib/oauth2_session.py", line 309, in refresh_token
    self.token = self._client.parse_request_body_response(r.text, scope=self.scope)
  File "/home/pi/.local/lib/python3.5/site-packages/oauthlib/oauth2/rfc6749/clients/base.py", line 411, in parse_request_body_response
    self.token = parse_token_response(body, scope=scope)
  File "/home/pi/.local/lib/python3.5/site-packages/oauthlib/oauth2/rfc6749/parameters.py", line 379, in parse_token_response
    validate_token_parameters(params)
  File "/home/pi/.local/lib/python3.5/site-packages/oauthlib/oauth2/rfc6749/parameters.py", line 389, in validate_token_parameters
    raise MissingTokenError(description="Missing access token parameter.")
oauthlib.oauth2.rfc6749.errors.MissingTokenError: (missing_token) Missing access token parameter.
pi@raspberrypi:~/nokia-weight-sync $ 
jschram commented 6 years ago

Same thing here; token is expired. It doesn't seem to request a new access token with the request token. According to the Nokia docs, the access_token expires after 4 hours, after which you'll need to request a new token with the refresh_token: Check this part of the docs

It seems that there's either no new access token requested before making a new request, or that the newly obtained token isn't used and/or stored. Also, the config never seems to update with a new token or token_expire.

magnific0 commented 6 years ago

@ltgustavsen @jschram thanks a lot for your reports. I'll look into it on a very short basis. @jschram The refreshing of the access token is actually setup correctly and handled by requests_oauthlib. Also before I pushed the new nokia.py I had tested it and it did refresh tokens, so I have no idea why it's failing now.

magnific0 commented 6 years ago

@ltgustavsen @jschram I have just pushed a new update. The problem was that the config was never updated with the new tokens after refresh. This only happened during "sync" mode. "last" mode was working fine, which I used to test on...

Hopefully this fixes everything. Please let me know!

jschram commented 6 years ago

Awesome; I've updated it, completed the setup and did a successful sync. Will check again tomorrow morning (when it has a new measurement from my scale) if it'll hold up.

ltgustavsen commented 6 years ago

For me the access token was expired again today :-(

Should the refresh token be updated in the config.ini file each time I run sync? I think only the file timestamp are updated this time:

pi@raspberrypi:~/nokia-weight-sync $ cp config.ini config.ini.backup
pi@raspberrypi:~/nokia-weight-sync $ ls -l config.ini*
-rw-r--r-- 1 pi pi 490 Aug 16 16:08 config.ini
-rw-r--r-- 1 pi pi 490 Aug 16 16:10 config.ini.backup
pi@raspberrypi:~/nokia-weight-sync $ python3 nokia-weight-sync.py  sync  garmin
Last measurement was already synced
Config file saved to config.ini
pi@raspberrypi:~/nokia-weight-sync $ ls -l config.ini*
-rw-r--r-- 1 pi pi 490 Aug 16 16:11 config.ini
-rw-r--r-- 1 pi pi 490 Aug 16 16:10 config.ini.backup
pi@raspberrypi:~/nokia-weight-sync $ diff config.ini*
pi@raspberrypi:~/nokia-weight-sync $ 

I also had the "Error code 401" on time today.

magnific0 commented 6 years ago

@ltgustavsen thanks for reporting back. If you got the error 401 again I can understand the refresh also failed. I hadn't started investigating 401 yet, but if the program throws an exception it will most definitely exit without updating the configuration file. Catching such exceptions and still handling saving the configuration file is on my to do list. I had just hoped that the 401 happened rarely. But at least for you it doesn't.

By the way the config file contents should only be updated if the token has expired. So your findings are normal. You can try this to force an update

  1. Get a current timestamp using: date +%s
  2. Edit config.ini and change token_expiry to the value from previous step. Save the config.
  3. Run the program and get the last measurement. ./nokia-weight-sync.py last

It should try to refresh the token just on the basis of it having passed expiration date.

Moreover if you edit nokia-weight-sync.py and add

import logging
logging.basicConfig(level=logging.DEBUG)

somewhere near the top (say right after import base64), you will get a lot of extra debug information from requests_oauthlib. Just be aware that there is also a lot of sensitive info in the debug log, should you want to share it!

ltgustavsen commented 6 years ago

Here is my log for two runs. One just before and one after the token experes: I have only hidden my consumer key, secret and userid.

pi@raspberrypi:~/nokia-weight-sync $ ./nokia-weight-sync.py last
DEBUG:requests_oauthlib.oauth2_session:Invoking 0 protected resource request hooks.
DEBUG:requests_oauthlib.oauth2_session:Adding token {'access_token': '833a76210739c97880906a74d97d58ca706ec88f', 'expires_in': '347', 'refresh_token': '04a98ebc040453fbc6b90075e76d1066499b1aa3', 'token_type': 'Bearer'} to request.
DEBUG:requests_oauthlib.oauth2_session:Requesting url https://api.health.nokia.com/measure using method GET.
DEBUG:requests_oauthlib.oauth2_session:Supplying headers {'Authorization': 'Bearer 833a76210739c97880906a74d97d58ca706ec88f'} and data None
DEBUG:requests_oauthlib.oauth2_session:Passing through key word arguments {'params': {'limit': 1, 'access_token': '833a76210739c97880906a74d97d58ca706ec88f', 'action': 'getmeas', 'userid': 'xxxxxxxx'}}.
DEBUG:urllib3.connectionpool:Starting new HTTPS connection (1): api.health.nokia.com:443
DEBUG:urllib3.connectionpool:https://api.health.nokia.com:443 "GET /measure?limit=1&access_token=833a76210739c97880906a74d97d58ca706ec88f&action=getmeas&userid=xxxxxxxx HTTP/1.1" 200 302
2018-08-17T05:25:06+00:00
Weight: 84.773
Height: None
Fat free mass: 69.32300000000001
Fat ratio: 18.225
Fat mass weight: 15.450000000000001
Diastolic blood pressure: None
Systolic blood pressure: None
Heart pulse: None
Temperature: None
Spo2: None
Body temperature: None
Skin temperature: None
Muscle mass: 65.89
Hydration: 44.69
Bone mass: 3.41
Pulse wave velocity: None
Config file saved to config.ini
pi@raspberrypi:~/nokia-weight-sync $ 
pi@raspberrypi:~/nokia-weight-sync $ ./nokia-weight-sync.py last
DEBUG:requests_oauthlib.oauth2_session:Invoking 0 protected resource request hooks.
DEBUG:requests_oauthlib.oauth2_session:Adding token {'expires_in': '-1351', 'access_token': '833a76210739c97880906a74d97d58ca706ec88f', 'token_type': 'Bearer', 'refresh_token': '04a98ebc040453fbc6b90075e76d1066499b1aa3'} to request.
DEBUG:requests_oauthlib.oauth2_session:Auto refresh is set, attempting to refresh at https://account.health.nokia.com/oauth2/token.
DEBUG:requests_oauthlib.oauth2_session:Adding auto refresh key word arguments {'client_id': 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', 'client_secret': 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'}.
DEBUG:requests_oauthlib.oauth2_session:Prepared refresh token request body grant_type=refresh_token&client_id=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&client_secret=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&params=%7B%27access_token%27%3A+%27833a76210739c97880906a74d97d58ca706ec88f%27%2C+%27limit%27%3A+1%2C+%27action%27%3A+%27getmeas%27%2C+%27userid%27%3A+%27xxxxxxxx%27%7D&refresh_token=04a98ebc040453fbc6b90075e76d1066499b1aa3
DEBUG:requests_oauthlib.oauth2_session:Requesting url https://account.health.nokia.com/oauth2/token using method POST.
DEBUG:requests_oauthlib.oauth2_session:Supplying headers {'Accept': 'application/json', 'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8'} and data {'grant_type': 'refresh_token', 'client_id': 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', 'client_secret': 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', 'params': "{'access_token': '833a76210739c97880906a74d97d58ca706ec88f', 'limit': 1, 'action': 'getmeas', 'userid': 'xxxxxxxx'}", 'refresh_token': '04a98ebc040453fbc6b90075e76d1066499b1aa3'}
DEBUG:requests_oauthlib.oauth2_session:Passing through key word arguments {'verify': True, 'timeout': None, 'auth': None, 'proxies': None, 'json': None}.
DEBUG:urllib3.connectionpool:Starting new HTTPS connection (1): account.health.nokia.com:443
DEBUG:urllib3.connectionpool:https://account.health.nokia.com:443 "POST /oauth2/token HTTP/1.1" 200 202
DEBUG:requests_oauthlib.oauth2_session:Request to refresh token completed with status 200.
DEBUG:requests_oauthlib.oauth2_session:Response headers were {'X-Content-Type-Options': 'nosniff', 'Content-Length': '202', 'X-Frame-Options': 'ALLOW-FROM https://dashboard.health.nokia.com/', 'Strict-Transport-Security': 'max-age=2592000', 'Referrer-Policy': 'strict-origin-when-cross-origin', 'Pragma': 'no-cache', 'Content-Security-Policy': "frame-ancestors 'self' https://dashboard.health.nokia.com/", 'Accept-Charset': 'utf-8', 'Cache-Control': 'no-store', 'Server': 'Apache', 'X-XSS-Protection': '1', 'Date': 'Fri, 17 Aug 2018 09:06:25 GMT', 'Content-Type': 'application/json', 'Accept': 'application/json', 'Accept-Encoding': 'gzip, deflate'} and content {"access_token":"af964e864cf5495ee339dddb20dc3cc975e6ecbe","expires_in":"10800","token_type":"Bearer","scope":"user.metrics","refresh_token":"43b49f850288d49db194fcfee8610fcd02bfd933","userid":xxxxxxxx}.
DEBUG:requests_oauthlib.oauth2_session:Invoking 0 token response hooks.
DEBUG:requests_oauthlib.oauth2_session:Updating token to {'expires_in': '10800', 'userid': xxxxxxxx, 'expires_at': 1534507585.2895215, 'scope': ['user.metrics'], 'refresh_token': '43b49f850288d49db194fcfee8610fcd02bfd933', 'access_token': 'af964e864cf5495ee339dddb20dc3cc975e6ecbe', 'token_type': 'Bearer'} using <bound method NokiaApi.set_token of <nokia.NokiaApi object at 0x75bd8330>>.
DEBUG:requests_oauthlib.oauth2_session:Requesting url https://api.health.nokia.com/measure using method GET.
DEBUG:requests_oauthlib.oauth2_session:Supplying headers {'Authorization': 'Bearer af964e864cf5495ee339dddb20dc3cc975e6ecbe'} and data None
DEBUG:requests_oauthlib.oauth2_session:Passing through key word arguments {'params': {'access_token': '833a76210739c97880906a74d97d58ca706ec88f', 'limit': 1, 'action': 'getmeas', 'userid': 'xxxxxxxx'}}.
DEBUG:urllib3.connectionpool:Starting new HTTPS connection (1): api.health.nokia.com:443
DEBUG:urllib3.connectionpool:https://api.health.nokia.com:443 "GET /measure?access_token=833a76210739c97880906a74d97d58ca706ec88f&limit=1&action=getmeas&userid=xxxxxxxx HTTP/1.1" 200 103
Traceback (most recent call last):
  File "./nokia-weight-sync.py", line 237, in <module>
    m = client_nokia.get_measures(limit=1)[0]
  File "/home/pi/nokia-weight-sync/nokia.py", line 164, in get_measures
    r = self.request('measure', 'getmeas', kwargs)
  File "/home/pi/nokia-weight-sync/nokia.py", line 152, in request
    raise Exception("Error code %s" % response['status'])
Exception: Error code 401
fernat commented 6 years ago

@magnific0 I'm seeing the same "Exception: Error code 401" error associated with an expired token.

magnific0 commented 6 years ago

@fernat @ltgustavsen thanks. I have been looking into this and it is clear to me what is going on: it is still attempting to make the original request with the old token, despite having refreshed correctly. I'm writing a fix now, but I want to be extra careful with testing this time, so I'll probably update tomorrow and post here.

magnific0 commented 6 years ago

@fernat @ltgustavsen the last commit should finally fix the issue. I have tested refreshing several times by letting it naturally expire and had no further issue. Hoping it works for you too!

jschram commented 6 years ago

I'm sorry to report that the issue wasn't fixed. It now seems to stall until a timeout occurs. This also happens when I try to setup the nokia app again:

To set a connection with Nokia Health you must have registered an application at https://account.health.nokia.com/partner/add_oauth2 .
Please enter the client id: *******
Please enter the consumer secret: *******
Please enter the callback url known by Nokia: https://localhost
Visit: https://account.health.nokia.com/oauth2_user/authorize2?response_type=code&client_id=*******&redirect_uri=https%3A%2F%2Flocalhost&scope=user.metrics&state=*******
and select your user and click "Allow this app".
Afterwards you will be redirected to your callback url with some additional parameters.
Example: https://your_original_callback?code=[code]&state=[state]
Please enter the full callback response url: https://localhost/?code=*******state=******* 

It stalls here for about 30 seconds, then fails:

Traceback (most recent call last):
  File "./nokia-weight-sync.py", line 219, in <module>
    setup_nokia( options, config )
  File "./nokia-weight-sync.py", line 84, in setup_nokia
    creds = auth.get_credentials(callback_url)
  File "/srv/users/jorick/schedules/nokia-weight-sync/nokia.py", line 84, in get_credentials
    client_secret=self.consumer_secret)
  File "/usr/local/lib/python3.4/dist-packages/requests_oauthlib/oauth2_session.py", line 244, in fetch_token
    self._client.parse_request_body_response(r.text, scope=self.scope)
  File "/usr/local/lib/python3.4/dist-packages/oauthlib/oauth2/rfc6749/clients/base.py", line 408, in parse_request_body_response
    self.token = parse_token_response(body, scope=scope)
  File "/usr/local/lib/python3.4/dist-packages/oauthlib/oauth2/rfc6749/parameters.py", line 379, in parse_token_response
    validate_token_parameters(params)
  File "/usr/local/lib/python3.4/dist-packages/oauthlib/oauth2/rfc6749/parameters.py", line 389, in validate_token_parameters
    raise MissingTokenError(description="Missing access token parameter.")
oauthlib.oauth2.rfc6749.errors.MissingTokenError: (missing_token) Missing access token parameter.
magnific0 commented 6 years ago

@jschram this is unfortunately a separate issue. Let's discuss it over at #7 I'll post my reply there shortly.

Last I checked refreshing is now running smoothly. I have had over 5 expired and refreshed tokens in the last 24 hours.

jschram commented 6 years ago

@magnific0 Alright, no problem; I'll monitor that issue instead.

magnific0 commented 6 years ago

Be aware I made some changes to the setup flow. @ltgustavsen I saw you're using a raspberrypi, so be careful choosing your callback url if you choose the new automated flow.

fernat commented 6 years ago

@magnific0 - things are working for me, thanks for the update and timely response.

jschram commented 6 years ago

Yes! Working here as well: 14 weights has been successfully updated to Garmin! Thanks!

magnific0 commented 6 years ago

Great! I'm leaving this issue open for a while so people having to migrate see it, at least until the end of the year (OAuth 1.0 closes November 31).

Thanks everyone for your patience and reporting these issues. I'm happy to tell you that directly thanks to the efforts here the upstream library python-nokia got updated to OAuth 2.0 as well. Let's hope it stays quiet for a while now :).