获取"缺少必需参数:refresh_token"使用带有谷歌oauth的Flask-dance进行



我正在使用Flask-dance来验证我的应用程序的用户。身份验证提供商是谷歌。

不时引发以下异常:

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

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

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

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

我的代码:(代码基于谷歌快速入门和烧瓶舞蹈问题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

知道如何解决吗?

从您的代码和烧瓶代码来看,您似乎正在尝试在撤销令牌之前将其删除,即使它已经无效。如果你想避免传递"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()

最新更新