我正在使用FLask Framework构建一个网站+后端,我使用Flask-OAuthlib对Google进行身份验证。身份验证后,后端需要定期扫描用户的Gmail。因此,目前用户可以对我的应用进行身份验证,然后存储access_token
和refresh_token
。 access_token
在一小时后过期,所以在这一小时内,我可以像这样获得userinfo:
google = oauthManager.remote_app(
'google',
consumer_key='xxxxxxxxx.apps.googleusercontent.com',
consumer_secret='xxxxxxxxx',
request_token_params={
'scope': ['https://www.googleapis.com/auth/userinfo.email', 'https://www.googleapis.com/auth/gmail.readonly'],
'access_type': 'offline'
},
base_url='https://www.googleapis.com/oauth2/v1/',
request_token_url=None,
access_token_method='POST',
access_token_url='https://accounts.google.com/o/oauth2/token',
authorize_url='https://accounts.google.com/o/oauth2/auth'
)
token = (the_stored_access_token, '')
userinfoObj = google.get('userinfo', token=token).data
userinfoObj['id'] # Prints out my google id
小时结束后,我需要使用refresh_token(我已经存储在我的数据库中)来请求新的access_token
。我尝试将the_stored_access_token
替换为the_stored_refresh_token
,但这只会给我Invalid Credentials
- 错误。
在this github issue中,我阅读了以下内容:
无论您如何获得访问令牌/刷新令牌(无论是通过授权代码授予还是资源所有者密码凭据),您都可以通过将刷新令牌作为refresh_token并将grant_type设置为'来以相同的方式进行交换。 refresh_token'
据我所知,我必须像这样创建一个远程应用程序:
google = oauthManager.remote_app(
'google',
# also the consumer_key, secret, request_token_params, etc..
grant_type='refresh_token',
refresh_token=u'1/xK_ZIeFn9quwvk4t5VRtE2oYe5yxkRDbP9BQ99NcJT0'
)
但这会导致TypeError: __init__() got an unexpected keyword argument 'refresh_token'
。所以从这里我有点失落。
有人知道如何使用refresh_token
获取新的access_token
吗?欢迎所有提示!
答案 0 :(得分:1)
这就是我为google获取新的access_token的方式:
from urllib2 import Request, urlopen, URLError
from webapp2_extras import json
import mimetools
BOUNDARY = mimetools.choose_boundary()
def refresh_token()
url = google_config['access_token_url']
headers = [
("grant_type", "refresh_token"),
("client_id", <client_id>),
("client_secret", <client_secret>),
("refresh_token", <refresh_token>),
]
files = []
edata = EncodeMultiPart(headers, files, file_type='text/plain')
headers = {}
request = Request(url, headers=headers)
request.add_data(edata)
request.add_header('Content-Length', str(len(edata)))
request.add_header('Content-Type', 'multipart/form-data;boundary=%s' % BOUNDARY)
try:
response = urlopen(request).read()
response = json.decode(response)
except URLError, e:
...
EncodeMultipart函数取自此处: https://developers.google.com/cloud-print/docs/pythonCode
请务必使用相同的BOUNDARY
答案 1 :(得分:0)
查看OAuthRemoteApp的源代码。构造函数不接受名为refresh_token的关键字参数。然而,它确实采用名为access_token_params
的{{1}}参数。
由于url相同,但授权类型不同。我想这样的电话会起作用:
an optional dictionary of parameters to forward to the access token url
答案 2 :(得分:0)
flask-oauthlib.contrib在remote_app中包含一个名为auto_refresh_url / refresh_token_url的参数,它完全符合您的想要做的事情。 An example如何使用它看起来像这样:
app= oauth.remote_app(
[...]
refresh_token_url='https://www.douban.com/service/auth2/token',
authorization_url='https://www.douban.com/service/auth2/auth',
[...]
)
但是我没有设法让它以这种方式运行。然而,如果没有contrib包,这是可能的。我的解决方案是捕获401 API调用并在refresh_token可用时重定向到刷新页面。 我的刷新端点代码如下所示:
@app.route('/refresh/')
def refresh():
data = {}
data['grant_type'] = 'refresh_token'
data['refresh_token'] = session['refresh_token'][0]
data['client_id'] = CLIENT_ID
data['client_secret'] = CLIENT_SECRET
# make custom POST request to get the new token pair
resp = remote.post(remote.access_token_url, data=data)
# checks the response status and parses the new tokens
# if refresh failed will redirect to login
parse_authorized_response(resp)
return redirect('/')
def parse_authorized_response(resp):
if resp is None:
return 'Access denied: reason=%s error=%s' % (
request.args['error_reason'],
request.args['error_description']
)
if isinstance(resp, dict):
session['access_token'] = (resp['access_token'], '')
session['refresh_token'] = (resp['refresh_token'], '')
elif isinstance(resp, OAuthResponse):
print(resp.status)
if resp.status != 200:
session['access_token'] = None
session['refresh_token'] = None
return redirect(url_for('login'))
else:
session['access_token'] = (resp.data['access_token'], '')
session['refresh_token'] = (resp.data['refresh_token'], '')
else:
raise Exception()
return redirect('/')
希望这会有所帮助。代码可以当然得到增强,肯定比捕捉401ers更优雅,但这是一个开始;)
另一件事:不要将令牌存储在Flask会话Cookie中。而是使用我在代码中执行的“Flask Session”中的服务器端会话!
答案 3 :(得分:0)
这就是我获取新访问令牌的方式。
from urllib2 import Request, urlopen, URLError
import json
import mimetools
BOUNDARY = mimetools.choose_boundary()
CRLF = '\r\n'
def EncodeMultiPart(fields, files, file_type='application/xml'):
"""Encodes list of parameters and files for HTTP multipart format.
Args:
fields: list of tuples containing name and value of parameters.
files: list of tuples containing param name, filename, and file contents.
file_type: string if file type different than application/xml.
Returns:
A string to be sent as data for the HTTP post request.
"""
lines = []
for (key, value) in fields:
lines.append('--' + BOUNDARY)
lines.append('Content-Disposition: form-data; name="%s"' % key)
lines.append('') # blank line
lines.append(value)
for (key, filename, value) in files:
lines.append('--' + BOUNDARY)
lines.append(
'Content-Disposition: form-data; name="%s"; filename="%s"'
% (key, filename))
lines.append('Content-Type: %s' % file_type)
lines.append('') # blank line
lines.append(value)
lines.append('--' + BOUNDARY + '--')
lines.append('') # blank line
return CRLF.join(lines)
def refresh_token():
url = "https://oauth2.googleapis.com/token"
headers = [
("grant_type", "refresh_token"),
("client_id", "xxxxxx"),
("client_secret", "xxxxxx"),
("refresh_token", "xxxxx"),
]
files = []
edata = EncodeMultiPart(headers, files, file_type='text/plain')
#print(EncodeMultiPart(headers, files, file_type='text/plain'))
headers = {}
request = Request(url, headers=headers)
request.add_data(edata)
request.add_header('Content-Length', str(len(edata)))
request.add_header('Content-Type', 'multipart/form-data;boundary=%s' % BOUNDARY)
response = urlopen(request).read()
print(response)
refresh_token()
#response = json.decode(response)
#print(refresh_token())
答案 4 :(得分:0)
使用您的var companies = db.Set<Company>()
.Include(e => e.Tags)
.ToList();
var includedTags = companies
.SelectMany(e => e.Tags)
.ToList();
var actualTags = db.Set<Company>()
.SelectMany(e => e.Tags)
.ToList();
,您可以获得一个新的refresh_token
,例如:
access_token