在 Flask 会话中存储 oAuth 状态令牌

Storing oAuth state token in Flask session

一些关于 oAuth 的教程使用 Flask 会话在 Flask 会话中存储状态参数和访问令牌。 (Brendan McCollam's very useful presentation from Pycon is an example)

我知道 Flask 将会话存储在客户端的 cookie 中,并且它们很容易公开(see Michael Grinberg's how-secure-is-the-flask-user-session)。我自己尝试过,并且能够看到令牌过期等

将状态和令牌存储在 Flask 会话中是否正确,还是应该将它们存储在其他地方?

代码示例:

@app.route('/login', methods=['GET'])
def login():
    provider = OAuth2Session(
                   client_id=CONFIG['client_id'],
                   scope=CONFIG['scope'],
                   redirect_uri=CONFIG['redirect_uri'])
    url, state = provider.authorization_url(CONFIG['auth_url'])
    session['oauth2_state'] = state
    return redirect(url)

@app.route('/callback', methods=['GET'])
def callback():
    provider = OAuth2Session(CONFIG['client_id'],
                             redirect_uri=CONFIG['redirect_uri'],
                             state=session['oauth2_state'])
    token_response = provider.fetch_token(
                        token_url=CONFIG['token_url'],
                        client_secret=CONFIG['client_secret'],
                        authorization_response=request.url)

    session['access_token'] = token_response['access_token']
    session['access_token_expires'] = token_response['expires_at']

    transfers = provider.get('https://transfer.api.globusonline.org/v0.10/task_list?limit=1')

    return redirect(url_for('index'))

@app.route('/')
def index():
    if 'access_token' not in session:
        return redirect(url_for('login'))
    transfers = requests.get('https://transfer.api.globusonline.org/v0.10/task_list?limit=1',
                             headers={'Authorization': 'Bearer ' + session['access_token']})
    return render_template('index.html.jinja2',
                           transfers=transfers.json())

我认为有些教程过度简化以显示更简单的代码。一个好的经验法则是仅将会话 cookie 用于您的应用程序和用户的浏览器必须知道的信息,并且不是私有的。这通常会转化为会话 ID 和可能的其他非敏感信息,例如语言选择。

应用这个经验法则,我会建议每个标记的旁边:

  1. 授权令牌:根据定义,此数据为用户和应用程序所知,因此将其公开在饼干。但是,一旦您获得了访问代码,就真的没有必要保留此令牌,因此我建议不要将其保留在本地或您的 cookie 中。

  2. 访问代码:此数据必须被视为机密,并且只能由您的应用程序和提供者知道。没有理由让任何其他方(包括用户)知道它,因此它不应包含在 cookie 中。如果您需要存储它,请将其保存在本地服务器中(可能在您的数据库中,引用您的用户会话 ID)。

  3. CSRF 状态令牌:理想情况下,此数据作为隐藏表单字段包含在内,并针对服务器端变量进行验证,因此 cookie 似乎是一种不必要的复杂化。但我不会担心这些数据在 cookie 中,因为它无论如何都是响应的一部分。

请记住有一些扩展,例如 flask-session,实际上相同的代码使用服务器端变量而不是 cookie 变量。