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

swo*_*ish 4 flask flask-sqlalchemy

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

我收到此错误:

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
Run Code Online (Sandbox Code Playgroud)

这也是我的表格如何在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)
Run Code Online (Sandbox Code Playgroud)

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

sin*_*boy 10

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


我是Flask-Dance的作者.此Cannot get OAuth token without an associated user异常仅出现在Flask-Dance的0.13.0及更高版本中.(CHANGELOG就在这里.)引入此更改pull请求有更多关于更改原因的上下文.

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

  1. 我想构建一个可以连接到一个特定服务的机器人,例如推特到特定帐户的Twitter机器人,或连接到特定Slack团队的Slack机器人.我希望这个机器人能够响应HTTP请求,所以它必须作为一个网站运行,即使我不希望人们直接使用这个网站.
  2. 我想建立一个用户可以登录的网站.用户需要使用用户名和密码在我的网站上创建一个帐户.之后,他们已经创建了一个账户,用户可以决定他们的帐户链接到其他OAuth的提供商,像谷歌或Facebook,解锁额外的功能.
  3. 我想构建一个用户可以登录的网站.用户只需登录GitHub(或任何其他OAuth提供商)即可创建自己的帐户.用户不需要为我的网站创建新密码.

用例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令牌相关联.该文档包含一些可用于此的示例代码.
  2. 禁用此检查,并告诉Flask-Dance可以在没有关联用户的情况下创建OAuth令牌.您可以通过设置禁用此检查user_required=False的上SQLAlchemyStorage对象.

我相信选项1是目前为止更好的解决方案,但它需要更多地了解Flask-Dance在幕后实际做的事情.我写了一些文档来描述如何处理多用户设置,这也讨论了这个问题.