Open lila opened 2 years ago
testing out with my minimal flask-dance-fitbit application: https://github.com/lila/flask-dance-fitbit
added a route /fitbitexpiretoken that does the following:
@app.route("/fitbitexpiretoken")
def fitbitexpire():
"""expires the fitbit token and forces a token refresh"""
if fitbit.authorized:
time_past = time() - 10
fitbit_bp.token['expires_at'] = time_past
print("access token: " + fitbit_bp.token['access_token'])
print("refresh_token: " + fitbit_bp.token['refresh_token'])
print("expiration time " + str(fitbit_bp.token['expires_at']))
print(" in " + str(fitbit_bp.token['expires_in']))
# this will fail due to expired token
try:
resp = fitbit.get("/1/user/-/profile.json",
headers={"Authorization": "Bearer " +
fitbit_bp.token["access_token"]},
)
print(resp)
print("access token: " + fitbit_bp.token['access_token'])
print("refresh_token: " + fitbit_bp.token['refresh_token'])
print("expiration time " + str(fitbit_bp.token['expires_at']))
print(" in " + str(fitbit_bp.token['expires_in']))
except Exception:
print("exception")
return "done"
When i hit this url, i now get:
access token: eyJhbGciOiJIUzI1NiJ9.eyJhdWQiOiIyMzhDREoiLCJzdWIiOiI2M05IWkMiLCJpc3MiOiJGaXRiaXQiLCJ0eXAiOiJhY2Nlc3NfdG9rZW4iLCJzY29wZXMiOiJyYWN0IHJwcm8iLCJleHAiOjE2NTQ1NjE2MzcsImlhdCI6MTY1NDUzMjgzN30.weJYnko13djbyJ2jZ3DH9OLvJet3Ge3TrjY9GwBPboI
refresh_token: a81d83ee3c0d20dbb573e44c2b19d656526700c5181f0bbec945d1aba59c35ef
expiration time 1654543853.5550377
in -10.001423
<Response [200]>
access token: eyJhbGciOiJIUzI1NiJ9.eyJhdWQiOiIyMzhDREoiLCJzdWIiOiI2M05IWkMiLCJpc3MiOiJGaXRiaXQiLCJ0eXAiOiJhY2Nlc3NfdG9rZW4iLCJzY29wZXMiOiJyYWN0IHJwcm8iLCJleHAiOjE2NTQ1NzI2NzcsImlhdCI6MTY1NDU0Mzg3N30.fjiIYYEwNFhH1IeRN2sCwk5j82JTcvjJrsbcq--u_Mc
refresh_token: 3e924b221c8cb3500f883935615852006cd3406b2f737a461f4395c67d917a42
expiration time 1654572663.698298
in 28799.894673
127.0.0.1 - - [06/Jun/2022 19:31:03] "GET /fitbitexpiretoken HTTP/1.1" 200 -
i manually expire the token then issue a get-profile api command. after that the token has been updated with a new refresh token and new expiration.
soo... the upshot is: flask-dance is doing the token refresh automatically.
How exactly does that happen? it feels a bit like magic to me, as i need a very specific authorization header when refreshing the token. i find it difficult to see how flask-dance or requests-oauth2lib figures that out.
Any explanation would be helpful.
Thanks in advance :-)
if i turn the debugging on for requests_oauthlib using:
import logging
import sys
log = logging.getLogger('requests_oauthlib')
log.addHandler(logging.StreamHandler(sys.stdout))
log.setLevel(logging.DEBUG)
then i see the following debug statements:
...
Auto refresh is set, attempting to refresh at https://api.fitbit.com/oauth2/token.
Encoding client_id "XXXXXX" with client_secret as Basic auth credentials.
Adding auto refresh key word arguments {}.
Prepared refresh token request body grant_type=refresh_token&scope=activity+profile&refresh_token=3e924b221c8cb3500f883935615852006cd3406b2f737a461f4395c67d917a42&allow_redirects=True
Requesting url https://api.fitbit.com/oauth2/token using method POST.
Supplying headers {'Accept': 'application/json', 'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8'} and data {'grant_type': 'refresh_token', 'scope': 'activity profile', 'refresh_token': '3e924b221c8cb3500f883935615852006cd3406b2f737a461f4395c67d917a42', 'allow_redirects': 'True'}
Passing through key word arguments {'json': None, 'auth': <requests.auth.HTTPBasicAuth object at 0x7f40c65cb8e0>, 'timeout': None, 'verify': True, 'proxies': None}.
Request to refresh token completed with status 200.
...
so it is definitely doing the token refresh. apparently the fitbit refresh api is more standard than i thought...
for the record, requests_oauthlib then uses requests.auth to build the authorization headers, then uses refresh_token() to make the call and update the tokens. this all happens behind the scenes of flask-dance.
All is good in the world of flask-dance. you can close the issue, but i thought i'd add all this here for completeness (and for when i forget)..
cheers and thanks for developing and maintaining flask-dance...
Hi,
I'm using the fitbit flask-dance contributed module. All is good, but when my token expires, then i would like to configure flask-dance and requests-oauthlib to automatically refresh the token if expired.
To do that with fitbit oauth, i use the same token url, but need to supply it with different body:
The authorization header is "Basic " + base64 encoded "client_id:client_secret". the body has grant_type and includes the refresh token.
I see that requests_oauthlib does have the mechanism to automatically refresh the token, see https://github.com/requests/requests-oauthlib/blob/master/requests_oauthlib/oauth2_session.py#L405 for example.
and it does check for expired tokens.
my question is: how can i configure the flask-dance fitbit module so that it does the right thing. All i see are two parameters,
fitbit_bp.auto_refresh_url
andfitbit_bp.auto_refresh_kwargs
(see https://github.com/singingwolfboy/flask-dance/blob/main/flask_dance/contrib/fitbit.py )i set
fitbit_bp.auto_refresh_url
to the current url for refreshing the tokens, and i tried settingfitbit_bp.auto_refresh_kwargs
in a few different ways, but i'm just not getting a valid response.any help is greatly appreciated. thanks in advance...
k