1

从阅读各种文档来看,oauth2 提供者似乎可以选择需要授权来刷新令牌请求。我正在使用似乎需要授权的 FitBit API。

我正在按照此处的说明刷新令牌requests-oauthlibhttps ://requests-oauthlib.readthedocs.io/en/latest/oauth2_workflow.html#refreshing-tokens

一些设置代码(不是我使用的,但你明白了:

>>> token = {
...     'access_token': 'eswfld123kjhn1v5423',
...     'refresh_token': 'asdfkljh23490sdf',
...     'token_type': 'Bearer',
...     'expires_in': '-30',     # initially 3600, need to be updated by you
...  }
>>> client_id = r'foo'
>>> refresh_url = 'https://provider.com/token'
>>> protected_url = 'https://provider.com/secret'

>>> # most providers will ask you for extra credentials to be passed along
>>> # when refreshing tokens, usually for authentication purposes.
>>> extra = {
...     'client_id': client_id,
...     'client_secret': r'potato',
... }

>>> # After updating the token you will most likely want to save it.
>>> def token_saver(token):
...     # save token in database / session
from requests_oauthlib import OAuth2Session
client = OAuth2Session(client_id, token=token, auto_refresh_url=refresh_url,
         auto_refresh_kwargs=extra, token_updater=token_saver)
r = client.get(protected_url)

但是,通过这个电话,我得到:

MissingTokenError: (missing_token) Missing access token parameter.

我知道我的令牌已过期,但为什么刷新不起作用?

4

2 回答 2

2

图书馆在这方面被打破了。见#379

您可以像这样解决它:

def _wrap_refresh(func):
    def wrapper(*args, **kwargs):
        kwargs['auth'] = (client_id, client_secret)
        kwargs.pop('allow_redirects', None)
        return func(*args, **kwargs)
    return wrapper

client = OAuth2Session(client_id, token=token,
                       auto_refresh_url=refresh_url, 
                       token_updater=token_saver)
client.refresh_token = _wrap_refresh(client.refresh_token)
于 2021-01-25T15:35:50.770 回答
0

编辑:下面仍然有一些有用的信息,但覆盖 auth 函数意味着我的实际 API 请求现在失败(即下面不是正确答案)我不确定我是如何获得上次尝试工作的一个请求。它可能只是返回了一个错误(在 json 中)而不是抛出一个错误,我只是假设没有引发错误意味着它实际上正在工作。请参阅 OrangeDog 提供的正确解决方法(直到修复库)。

好吧,就在抛出 MissingTokenError 之前,我检查了 FitBit 服务器的响应。事实证明,我收到一个错误,说身份验证不正确。

这也许是一个有用的观点,值得一提。当响应不包含预期的令牌时,似乎会发生 MissingTokenError。如果您可以调试并更仔细地查看响应,您可能会发现服务器提供了有关您的请求格式错误的原因的更多详细信息。我去了错误的位置并添加了一条打印语句。这让我可以看到来自 FitBit 的 JSON 消息。无论如何,这种方法可能对其他获得 MissingTokenError 的人有用。

    if not 'access_token' in params:
        print(params)
        raise MissingTokenError(description="Missing access token parameter.")

无论如何,经过进一步的调试,没有设置身份验证。此外,我的客户 ID 和秘密已发布在正文中(这可能不是问题)。在 FitBit 示例中,客户端 ID 和密码未发布在正文中,而是通过身份验证传递。所以我需要客户端将身份验证传递给 FitBit。

那么问题来了,我如何进行身份验证。目前缺乏这方面的文件。但是,查看会话对象时,我发现正在设置的 .auth 属性和对问题的引用(#278)。在该问题中,提供了手动身份验证设置的解决方法(如下所示):https ://github.com/requests/requests-oauthlib/issues/278

请注意,oauth 会话继承自 requests 会话,因此对于非常了解请求的人来说,这可能很明显......

无论如何,解决方案只是在初始化会话后设置 auth 参数。由于 FitBit 在正文中不需要客户端 ID 和密码,因此我也删除了额外的传递(同样,这可能是一个小问题,不会真正影响事情):

import os
import json
from requests.auth import HTTPBasicAuth
from requests_oauthlib import OAuth2Session

client_id = ""
client_secret = ""

with open("tokens.json", "r") as read_file:
    token = json.load(read_file)       

save_file_path = os.path.abspath('tokens.json')

refresh_url = 'https://api.fitbit.com/oauth2/token'

def token_saver(token):    
    with open(save_file_path, "w") as out_file:
        json.dump(token, out_file, indent = 6) 

#Note, I've removed the 'extras' input
client = OAuth2Session(client_id, token=token, auto_refresh_url=refresh_url, token_updater=token_saver)
#This was the magic line ...
auth = HTTPBasicAuth(client_id, client_secret)
client.auth = auth

url = 'https://api.fitbit.com/1.2/user/-/sleep/date/2021-01-01-2021-01-23.json'
wtf = client.get(url)

好的,我想我正确地复制了该代码,目前我的结果有点混乱。关键部分很简单:

client.auth = auth

客户端启动后。

请注意,我的令牌包含一个expires_at字段。我不认为会议处理expires_in确切时间方面的问题。换句话说,我认为expires_in只有在其值小于 0 时才会导致刷新。我认为它不会查看对象创建的时间并启动计时器或设置属性以了解expires_in相对于什么。expires_at另一方面,该字段似乎提供(我认为)一个经过检查以确保令牌在请求时没有过期的字段,因为expires_at这是一个真实的、非相对的时间。这是我的令牌字典(带有假令牌和 user_id):

{'access_token': '1234',
 'expires_in': 28800,
 'refresh_token': '5678',
 'scope': ['heartrate',
  'profile',
  'settings',
  'nutrition',
  'location',
  'weight',
  'activity',
  'sleep',
  'social'],
 'token_type': 'Bearer',
 'user_id': 'abcd',
 'expires_at': 1611455442.4566112}
于 2021-01-24T19:12:19.560 回答