1

我正在使用 Flask-dance 来对我的应用程序的用户进行身份验证。身份验证提供者是 Google。

不时会引发以下异常:

2019-08-09 08:07:26 default[20190809t105407]  "GET / HTTP/1.1" 500
2019-08-09 08:07:26 default[20190809t105407]  -- 0 --
2019-08-09 08:07:26 default[20190809t105407]  -- 1 --
2019-08-09 08:07:27 default[20190809t105407]  InvalidClientIdError was caught: (invalid_request) Missing required parameter: refresh_token

环顾这个问题,我可以找到两个方向:

  1. offline=True在创建 Google 蓝图时使用
  2. 实现错误处理程序TokenExpiredError

我两者都做了并将我的应用程序部署到 GAE,但我仍然面临同样的错误。从堆栈跟踪中,我了解到正在调用错误处理程序,但是在代码尝试恢复时引发了“refresh_token”

我的代码:(代码基于Google QuickStart和 Flask-dance issue 143

import oauthlib
from flask import Flask, redirect, url_for, flash, session, current_app
from flask_dance.contrib.google import make_google_blueprint, google
import os
import time
from oauthlib.oauth2.rfc6749.errors import InvalidClientIdError, TokenExpiredError

GOOGLE_CLIENT_ID = os.environ.get("GOOGLE_CLIENT_ID", None)
GOOGLE_CLIENT_SECRET = os.environ.get("GOOGLE_CLIENT_SECRET", None)

app = Flask(__name__)
app.secret_key = "TODO_TODO"
blueprint = make_google_blueprint(
    client_id=GOOGLE_CLIENT_ID,
    client_secret=GOOGLE_CLIENT_SECRET,
    offline=True
)
app.register_blueprint(blueprint, url_prefix="/login")


@app.route('/logout', methods=['GET'])
def logout():
    _revoke_token_and_empty_session()
    return redirect(url_for('app.index'))


def _revoke_token_and_empty_session():
    print('inside _revoke_token_and_empty_session')
    if google.authorized:
        try:
            google.get(
                'https://accounts.google.com/o/oauth2/revoke',
                params={
                    'token':
                        current_app.blueprints['google'].token['access_token']},
            )
        except TokenExpiredError:
            pass
        except InvalidClientIdError:
            # Our OAuth session apparently expired. We could renew the token
            # and logout again but that seems a bit silly, so for now fake
            # it.
            pass
    session.clear()


@app.errorhandler(oauthlib.oauth2.rfc6749.errors.TokenExpiredError)
def token_expired(_):
    print('In TokenExpiredError')
    del blueprint.token
    _revoke_token_and_empty_session()
    flash('Your session had expired. Please submit the request again',
          'error')
    return redirect(url_for('app.index'))


@app.route("/")
def index():
    print('-- 0 --')
    if not google.authorized:
        return redirect(url_for("google.login"))
    print('-- 1 --')
    user_info_url = 'https://openidconnect.googleapis.com/v1/userinfo'
    try:
        resp = google.get(user_info_url)
    except InvalidClientIdError as e:
        #
        # Here is the problem
        #
        print('InvalidClientIdError was caught: {}'.format(str(e)))
        return 'Having an InvalidClientIdError issue: {}'.format(str(e)), 500
    else:
        print('-- 2 --')
        user_info = resp.json()
        return "You are {user_name} on Google. Time: {t}".format(user_name=user_info['name'], t=time.time())


if __name__ == "__main__":
    app.run()

我目前的理解是TokenExpiredError被捕获并index调用了函数。当函数尝试调用resp = google.get(user_info_url)InvalidClientIdError: (invalid_request) Missing required parameter: refresh_token引发。

知道如何解决吗?

4

1 回答 1

0

从您的代码和烧瓶代码来看,您似乎正在尝试在撤销令牌之前删除它,即使它已经无效。如果您想避免传递“offline=True”,请尝试简化代码,因为不需要闪烁任何内容(用户将被重定向),并且令牌已经无效,因此撤销没有多大意义它要么。我也只设置了一个错误处理程序。

这是对我有用的代码:

import oauthlib
from flask import Flask, redirect, url_for, flash, session, current_app
from flask_dance.contrib.google import make_google_blueprint, google
import os
import time
from oauthlib.oauth2.rfc6749.errors import InvalidClientIdError, TokenExpiredError

GOOGLE_CLIENT_ID = os.environ.get("GOOGLE_CLIENT_ID", None)
GOOGLE_CLIENT_SECRET = os.environ.get("GOOGLE_CLIENT_SECRET", None)

app = Flask(__name__)
app.secret_key = "TODO_TODO"
blueprint = make_google_blueprint(
    client_id=GOOGLE_CLIENT_ID,
    client_secret=GOOGLE_CLIENT_SECRET,
    offline=True
)
app.register_blueprint(blueprint, url_prefix="/login")


@app.route('/logout', methods=['GET'])
def logout():
    """
    This endpoint tries to revoke the token
    and then it clears the session
    """
    if google.authorized:
        try:
            google.get(
                'https://accounts.google.com/o/oauth2/revoke',
                params={
                    'token':
                        current_app.blueprints['google'].token['access_token']},
            )
        except TokenExpiredError:
            pass
        except InvalidClientIdError:
            # Our OAuth session apparently expired. We could renew the token
            # and logout again but that seems a bit silly, so for now fake
            # it.
            pass
    _empty_session()
    return redirect(url_for('app.index'))


def _empty_session():
    """
    Deletes the google token and clears the session
    """
    if 'google' in current_app.blueprints and hasattr(current_app.blueprints['google'], 'token'):
        del current_app.blueprints['google'].token
    session.clear()


@app.errorhandler(oauthlib.oauth2.rfc6749.errors.TokenExpiredError)
@app.errorhandler(oauthlib.oauth2.rfc6749.errors.InvalidClientIdError)
def token_expired(_):
    _empty_session()
    return redirect(url_for('app.index'))

@app.route("/")
def index():
    print('-- 0 --')
    if not google.authorized:
        return redirect(url_for("google.login"))
    print('-- 1 --')
    user_info_url = 'https://openidconnect.googleapis.com/v1/userinfo'
    resp = google.get(user_info_url)
    user_info = resp.json()
    return "You are {user_name} on Google. Time: {t}".format(user_name=user_info['name'], t=time.time())


if __name__ == "__main__":
    app.run()
于 2019-09-06T22:36:40.733 回答