Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

getting flask-dance to auto refresh my expired tokens #391

Open
lila opened this issue Jun 2, 2022 · 2 comments
Open

getting flask-dance to auto refresh my expired tokens #391

lila opened this issue Jun 2, 2022 · 2 comments

Comments

@lila
Copy link
Contributor

lila commented Jun 2, 2022

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:

Authorization: Basic Y2xpZW50X2lkOmNsaWVudCBzZWNyZXQ=
Content-Type: application/x-www-form-urlencoded

grant_type=refresh_token&refresh_token=abcdef01234567890abcdef01234567890abcdef01234567890abcdef0123456

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 and fitbit_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 setting fitbit_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

@lila
Copy link
Contributor Author

lila commented Jun 6, 2022

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 :-)

  • k

@lila
Copy link
Contributor Author

lila commented Jun 6, 2022

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...

  • k

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant