flask - flask_dance : Cannot get OAuth token without an associated user

标签 flask flask-sqlalchemy

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

我收到此错误:
Cannot get OAuth token without an associated user
在我完成蓝图和sqlalchemy后端之间的连接之前,该应用程序运行正常,如果我删除了google_blueprint.backend行,该错误将消失。

这是我的 __init__.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

这也是我在 models.py 中如何组织它们的表格:
class User(db.Model, UserMixin):
    id = db.Column(db.Integer(), primary_key=True)
    image = db.Column(db.String(), nullable=True)
    fullname = db.Column(db.String())
    username = db.Column(db.String(), unique=True)
    password = db.Column(db.String())
    email = db.Column(db.String(), unique=True)

class OAuth(OAuthConsumerMixin, db.Model):
    user_id = db.Column(db.Integer(), db.ForeignKey(User.id))
    user = db.relationship(User)

请任何帮助将不胜感激:)

最佳答案

TL; DR:您可以通过在SQLAlchemyStorage对象上设置user_required=False来禁用此异常。但是,引发异常是有原因的,如果您仅以此方式禁用它,则数据库可能会进入意外状态,其中某些OAuth token 未链接到用户。有一种更好的方法可以解决此问题。继续阅读以获取详细信息。

我是Flask-Dance的作者。仅在Flask-Dance的0.13.0及更高版本中存在此Cannot get OAuth token without an associated user异常。 (CHANGELOG is here.)pull request introducing this change具有更多有关更改原因的上下文。

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

  • 我想构建一个可以连接到一项特定服务的机器人,例如一个Twitter机器人(可以将其鸣叫到一个特定的帐户),或者一个Slack机器人(可以将其连接到一个特定的Slack团队)。我希望该机器人能够响应HTTP请求,因此即使我不希望人们直接使用此网站,它也必须作为一个网站运行。
  • 我想建立一个用户可以登录的网站。用户需要使用用户名和密码在我的网站上创建一个帐户。创建帐户后,用户可以决定将其帐户链接到其他OAuth提供商,例如Google或Facebook,以解锁其他功能。
  • 我想建立一个用户可以登录的网站。用户应该可以简单地通过使用GitHub(或任何其他OAuth提供商)登录来创建其帐户。用户不需要为我的网站创建新密码。

  • 用例1最简单:不要将useruser_id参数传递给SQLAlchemyStorage,并且将假定您的应用程序不使用多个用户帐户。这意味着您的网站只能链接到远程服务上的一个特定帐户:只有一个Twitter帐户,只有一个Slack团队等。

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

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

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

    从0.13.0版开始,Flask-Dance会检测到此问题并引发异常,而不是在没有关联的本地用户帐户的情况下将OAuth token 保存到数据库中。有两种方法可以解决此问题:
  • 重写代码以手动创建用户帐户并将其与OAuth token 关联。 The documentation contains some example code you can use for this.
  • 禁用此检查,并告诉Flask-Dance在没有关联用户的情况下创建OAuth token 是可以的。您可以通过在 user_required=False 对象上设置SQLAlchemyStorage来禁用此检查。

  • 我相信到目前为止,选择1是更好的解决方案,但是它需要对Flask-Dance在幕后的实际操作有更多的了解。 I've written some documentation that describes how to handle multi-user setups, which discusses this problem as well.

    关于flask - flask_dance : Cannot get OAuth token without an associated user,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47643448/

    相关文章:

    python - 如何从同一列中获取所有值?

    Flask - 动态创建文件并将其上传到文件夹中

    python - 在 heroku 上部署时运行一次 python 脚本

    python - MongoDB、MongoEngine : unique list items

    python - 如何创建指向另一个 html 页面的链接?

    html - 使用 jinja2 在表中实现多行

    javascript - 根据用户点击的内容重定向到其中包含 id 的 url

    模型的 Python 干净导入 - SQL Alchemy

    python - 如何根据同一模型的其他两个列设置sqlalchemy中一列的值?

    python - 使用 flask-sqlalchemy 创建应用程序模式