flask_dance:没有关联用户,无法获取OAuth令牌

时间:2017-12-04 23:06:17

标签: flask flask-sqlalchemy

我想使用我的应用程序迁移flask_dance,以便用户使用谷歌和其他社交网络进行授权。

我收到此错误:

return

在我建立蓝图和sqlalchemy后端之间的连接之前,应用程序运行正常,如果我删除Cannot get OAuth token without an associated user行,则错误消失。

这是我的 __ init __。py

google_blueprint.backend

这也是我的表格,我如何在 models.py

中组织它们
import os
from flask import Flask, redirect, url_for, current_app
from flask_login import current_user
from develop.models import (
    db,
    User,
    OAuth
)

from flask_dance.contrib.google import make_google_blueprint
from flask_dance.consumer.backend.sqla import SQLAlchemyBackend
from flask_dance.consumer import oauth_authorized
from sqlalchemy.orm.exc import NoResultFound

def create_app(config_object):
    app = Flask(__name__)
    app.config.from_object(config_object)
    db.init_app(app)
    login_manager.init_app(app)

    google_blueprint = make_google_blueprint(
        client_id=app.config['GOOGLE_CLIENT_ID'],
        client_secret=app.config['GOOGLE_CLIENT_SECRET'],
        scope=["profile", "email"]
    )
    app.register_blueprint(google_blueprint, url_prefix='/login')
    @oauth_authorized.connect_via(google_blueprint)
    def google_logged_in(blueprint, token):
        resp = blueprint.session.get("/oauth2/v2/userinfo")
        if resp.ok:
            account_info_json = resp.json()
            email = account_info_json['email']
            query = User.query.filter_by(email=email)
            try:
                user = query.one()
            except NoResultFound:
                user = User()
                user.image = account_info_json['picture']
                user.fullname = account_info_json['name']
                user.username = account_info_json['given_name']
                user.email = account_info_json['email']
                db.session.add(user)
                db.session.commit()
                login_user(get_user, remember=True)

                identity_changed.send(
                    current_app._get_current_object(),
                    identity=Identity(get_user.id)
                )

    @login_manager.user_loader
    def load_user(userid):
        return User.query.get(userid)

    google_blueprint.backend = SQLAlchemyBackend(OAuth, db.session, user=current_user)

    return app

请提供任何帮助:)

1 个答案:

答案 0 :(得分:8)

TL; DR:您可以通过在SQLAlchemyStorage对象上设置user_required=False来停用此例外。但是,由于某种原因引发异常,如果您只是像这样禁用它,您的数据库可能会进入意外状态,其中某些OAuth令牌未链接到用户。有一个更好的方法来解决这个问题。请继续阅读。

我是Flask-Dance的作者。此Cannot get OAuth token without an associated user例外仅出现在Flask-Dance的0.13.0及更高版本中。 (CHANGELOG is here.pull request introducing this change有更多关于更改原因的背景信息。

使用OAuth有几种不同的方法。以下是一些示例用例,所有这些都是Flask-Dance支持的:

  1. 我想构建一个可以连接到一个特定服务的机器人,例如推特到特定帐户的Twitter机器人,或连接到特定Slack团队的Slack机器人。我希望这个机器人能够响应HTTP请求,所以它必须作为一个网站运行,即使我不希望人们直接使用这个网站。
  2. 我想建立一个用户可以登录的网站。用户需要使用用户名和密码在我的网站上创建一个帐户。 之后,他们创建了一个帐户,用户可能会决定将其帐户与其他OAuth提供商(如Google或Facebook)相关联,以解锁其他功能。
  3. 我想构建一个用户可以登录的网站。用户只需登录GitHub(或任何其他OAuth提供商)即可创建自己的帐户。用户不需要为我的网站创建新密码。
  4. 用例1是最简单的:不要将useruser_id参数传递给SQLAlchemyStorage,并假设您的应用程序不使用多个用户帐户。这意味着您的网站只能链接到远程服务上的一个特定帐户:只有一个Twitter帐户,只有一个Slack团队等。

    用例2也很简单:将useruser_id参数传递给SQLAlchemyStorage。 Flask-Dance会自动将OAuth令牌保存到您的数据库中,并将其链接到当前登录的用户。

    用例3更复杂,因为它涉及同时自动创建OAuth令牌和本地用户帐户。不同的应用程序对创建用户帐户有不同的要求,Flask-Dance无法知道这些要求是什么。因此,Flask-Dance无法自动处理此用例。您必须挂钩oauth_authorized信号,创建用户帐户并手动将其与OAuth令牌关联,然后返回False告诉Flask-Dance不要尝试自动处理OAuth令牌。

    在版本0.13.0之前,可能会在数据库中意外创建未与任何用户链接的OAuth令牌。在用例3中,OAuth令牌是在该用户的本地用户帐户之前创建的,因此Flask-Dance会将OAuth令牌保存到数据库而不包含任何链接的本地用户帐户。您可以使用oauth_authorized处理程序将OAuth令牌与本地用户帐户关联,但如果您的代码有问题并引发异常,则OAuth令牌可能会保留在您的数据库中,永远与任何用户无关。

    从版本0.13.0开始,Flask-Dance检测到此问题并引发异常,而不是在没有关联的本地用户帐户的情况下将OAuth令牌保存到数据库。有两种方法可以解决此问题:

    1. 重写您的代码以手动创建用户帐户并将其与OAuth令牌相关联。 The documentation contains some example code you can use for this.
    2. 禁用此检查,并告诉Flask-Dance可以在没有关联用户的情况下创建OAuth令牌。您可以通过在SQLAlchemyStorage对象上设置user_required=False来停用此检查。
    3. 我相信选项1是目前为止更好的解决方案,但它需要更多地了解Flask-Dance在幕后实际做的事情。 I've written some documentation that describes how to handle multi-user setups, which discusses this problem as well.