【发布时间】:2018-07-04 03:19:56
【问题描述】:
前段时间我用python写了一个应用程序,它使用了谷歌的python gdata api。 我开始关注 gdata_api 示例,它运行良好。 在某些时候,谷歌关闭了 uid/pwd 身份验证,并强制执行 OAuth2。 所以我修改了应用程序以使用 OAuth2 运行。
它已经工作了几年,直到有一天它停止了,并在尝试请求访问令牌时开始返回错误 400(= 错误请求)(要清楚的是来自 https://www.googleapis.com/oauth2/v3/token 的那个) . 这不是第一次发生(意味着在一个人请求一个新的授权令牌并第一次使用它之后),而是每次之后。
我看到帖子说他们修复了重定向 uri,然后他们让它工作了,但他们没有写出要使用什么。 我也不知道这是否是正确的解决方法。 我的 OAuth2 实现的精简版本如下(我再说一遍,它多年来一直运行良好):
import sys
import os
# WARNING!! you need also TLSLITE inside gdata/oauth subdir.
# This is how supported google shits are nowadays.
# See: https://github.com/google/gdata-python-client/issues/44
sys.path.append("~/gdatalib/gdata-python-client-master/src") #Quick and dirty way to get the latest version in. It doesnt make much of a difference anyway
import gdata
import gdata.contacts.data
import gdata.contacts.client
import urllib
import urllib2
import json
import subprocess
clientId = "" # your app's client ID , get one at https://console.developers.google.com/projectselector/apis/credentials?supportedpurview=project
clientSecret = "" # your app's client secret
oauthPath = ""
userAgent = "fuBar"
class OAuth2Auth(object):
def __init__(self, sUserName, sAccountPassword):
#gather all data
self.username = sUserName.split("@")[0] # remove @whatever, if any
self.password = sAccountPassword
self.clientid = clientId
self.clientsecret = clientSecret
self.rediruri = '''urn:ietf:wg:oauth:2.0:oob'''
self.authrequesturl = ("https://accounts.google.com/o/oauth2/auth?scope=https://www.google.com/m8/feeds/"
"&redirect_uri=%s"
"&response_type=code"
"&client_id=%s"
"&login_hint=%s") # % (redir_uri,client_id,sAccountName+"@gmail.com")
self.oauth2_endpoint = "https://www.googleapis.com/oauth2/v3/token"
self.tokensPath = os.path.join(oauthPath, self.username)
#print self.tokensPathata should be a b
self.accessToken = None
self.refreshToken = None
self._getTokens()
#create an OAuth2Token
print "Creating Oauth2Token...",
self.oauth2token = gdata.gauth.OAuth2Token(client_id = self.clientid,
client_secret = self.clientsecret,
scope = 'https://www.google.com/m8/feeds/',
user_agent = userAgent,
access_token = self.accessToken,
refresh_token = self.refreshToken)
print " done."
pass
def __del__(self):
#check that the access token in the OAuth2Token is the same as the read one.
if (self.accessToken != self.oauth2token.access_token):
#if not, update the file
print "Access token has been updated by gdata_api. Updating the storage."
self.accessToken = self.oauth2token.access_token
self._storeTokens()
pass
def _storeTokens(self):
if self.accessToken and self.refreshToken:
f= open(self.tokensPath,'w+');
f.seek(0);
f.truncate();
data = [ self.accessToken + '\n', self.refreshToken+'\n' ]
f.writelines(data)
f.close()
def _readTokens(self):
if not os.path.isfile(self.tokensPath):
raise Exception('Expecting to find token file, but the file is not present!')
f= open(self.tokensPath,'r')
tokenlist = [ l.rstrip('\n') for l in f.readlines() ]
f.close()
if ( len(tokenlist) < 2 ):
raise Exception('Not enough data in token file!')
self.accessToken = tokenlist[0]
self.refreshToken = tokenlist[1]
def _getTokens(self):
if not os.path.isfile(self.tokensPath):
print "TokenPath doesn't exist. requesting new tokens"
self._requestNewTokens()
self._storeTokens()
else:
print "TokenPath exists"
self._readTokens()
def _requestNewTokens(self):
#print '\nSTEP 1: Create OAuth2 request token URL.'
request_url = self.authrequesturl % (self.rediruri,self.clientid,self.username+"@gmail.com")
#print '\nSTEP 2: Spawn grob with adequate URL.'
CHROME = os.path.join('C:\\', 'Program Files (x86)', 'Google', 'Chrome', 'Application', 'chrome.exe')
CHROME = "/usr/bin/google-chrome" # for linux
#CHROME = "" # or use whatever browser
#subprocess.call([CHROME, '--incognito', request_url]) #use this the first time, after, you can also hardcode the token below
request_token = """<your request token here to avoid storing and retrieving it>""" # You can hardcode the token here, if you got, and comment the line above spawning a chrome
#print 'Request Token fetched: %s' % request_token
#print '\nSTEP 3: Exchange auth token for access and refresh OAuth2 tokens.'
request_args = { 'code':request_token,
'client_id': self.clientid ,
'client_secret': self.clientsecret,
'redirect_uri':self.rediruri,
'grant_type':'authorization_code' }
data = urllib.urlencode(request_args)
fullUrl = self.oauth2_endpoint + "/" + data
request = urllib2.Request(self.oauth2_endpoint, data)
try:
response = urllib2.urlopen(request) # ===== FAILS HERE =====
json_data = response.read()
parsed_json = json.loads(json_data)
self.accessToken = parsed_json['access_token']
self.refreshToken =parsed_json['refresh_token']
print parsed_json
except Exception, e:
print fullUrl
print request.get_full_url()
print request.get_selector()
print e
def main():
testuser="someuser@gmail.com"
testpass="somepass"
manager = OAuth2Auth(testuser,testpass)
if __name__ == '__main__':
main()
根据https://developers.google.com/identity/protocols/OAuth2WebServer#offline,应该在请求访问令牌时添加access_type = "offline",以获得刷新令牌。但是,我试过了,它不起作用。
【问题讨论】:
标签: python oauth oauth-2.0 google-oauth gdata-api