[英]Gmail API error when access token expires (Python, Django): HttpAccessTokenRefreshError invalid_client: The OAuth client was not found
我正在建立Django专案。 我需要通过Gmail的API发送电子邮件,因此我创建了一个项目,获得了API密钥,oauth凭据等。
我设法完成了“授权”部分,即用户登录所在的位置,授予您阅读+发送电子邮件的权限(据我所知,access_type = offline),并且您获得了access_token。 我将用户的“ access_token”,“ expires_in”和“ refresh_token”字段保存在数据库中。
然后,我可以阅读用户电子邮件的标签,以他的名字发送电子邮件,等等。没问题。
一旦access_token过期(因此,在用户授予权限的一个小时之后),就会出现问题。
尝试发送电子邮件时,我得到的错误是:
/ test_gmail_api3 /上的HttpAccessTokenRefreshError
invalid_client:找不到OAuth客户端。
我浏览了很多相关的stackoverflow问题,并尝试更改了许多不同的东西,但是没有任何效果。 唯一有效的方法是从我的测试gmail帐户手动撤消访问权限,再次授予访问权限,然后它可以正常运行...直到access_token再次到期。
这是错误:
这些是代码(t)的相关部分:
这是代码的相关部分:
用于向用户征求同意/许可的视图(效果很好,使用以下代码重定向到test_gmail_api2:
def test_gmail_api(request):
flow = OAuth2WebServerFlow(client_id='<< edited to keep secret >>.apps.googleusercontent.com',
client_secret='<< edited to keep secret >>MpNLDa',
scope=('https://www.googleapis.com/auth/gmail.readonly ' + 'https://www.googleapis.com/auth/gmail.send'),
redirect_uri='http://localhost:8000/test_gmail_api2/')
auth_uri = flow.step1_get_authorize_url()
return redirect(auth_uri)
用于使用Google代码获取access_token和refresh_token的视图(效果很好,我获取了令牌并将其另存为字符串):
def test_gmail_api2(request):
flow = OAuth2WebServerFlow(client_id='<< edited to keep secret >>.apps.googleusercontent.com',
client_secret='<< edited to keep secret >>MpNLDa',
scope=('https://www.googleapis.com/auth/gmail.readonly ' + 'https://www.googleapis.com/auth/gmail.send'),
redirect_uri='http://localhost:8000/test_gmail_api2/')
credentials = flow.step2_exchange(request.GET.get('code', ''))
current_profile = Profile.objects.get(email_address='none@test.com')
current_profile.access_token = credentials.get_access_token(http=None).access_token
current_profile.access_token_expiration = credentials.get_access_token(http=None).expires_in
string_credentials = credentials.to_json()
json_credentials = json.loads(string_credentials)
current_profile.refresh_token = json_credentials['refresh_token']
current_profile.save()
service = build('gmail', 'v1', http=credentials.authorize(Http()))
# Call the Gmail API
test_response = ""
results = service.users().labels().list(userId='me').execute()
labels = results.get('labels', [])
if not labels:
test_response += 'No labels found.'
else:
test_response += 'Labels:'
for label in labels:
test_response += label['name'] + "<br>"
response = service.users().messages().list(userId='me',
q='has:attachment').execute()
messages = []
if 'messages' in response:
messages.extend(response['messages'])
test_response += string_credentials
return HttpResponse(test_response)
最后,该视图将读取所有标签并发送电子邮件:
def test_gmail_api3(request):
client_id='<< edited to keep secret >>.apps.googleusercontent.com',
client_secret='<< edited to keep secret >>MpNLDa'
current_profile = Profile.objects.get(email_address='none@test.com')
refresh_token = current_profile.refresh_token
access_token = current_profile.access_token
expires_at = current_profile.access_token_expiration
some_user_agent = 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/33.0.1750.517 Safari/537.36'
cred = oauth2client.client.GoogleCredentials(access_token,client_id,client_secret,
refresh_token,expires_at,"https://accounts.google.com/o/oauth2/token",some_user_agent) # tried access_token=None and token_expiry=None but it doesn't work
http = cred.authorize(httplib2.Http())
service = build('gmail', 'v1', http=cred.authorize(Http()))
# Call the Gmail API
test_response = ""
string_credentials = cred.to_json()
json_credentials = json.loads(string_credentials)
test_response += string_credentials + '<br>'
results = service.users().labels().list(userId='me').execute()
labels = results.get('labels', [])
if not labels:
test_response += 'No labels found.'
else:
test_response += 'Labels:'
for label in labels:
test_response += label['name'] + "<br>"
response = service.users().messages().list(userId='me',
q='has:attachment').execute()
messages = []
if 'messages' in response:
messages.extend(response['messages'])
test_response += cred.to_json() + '<br>'
user_id='me'
threads = service.users().threads().list(userId=user_id).execute().get('threads', [])
for thread in threads:
tdata = service.users().threads().get(userId=user_id, id=thread['id']).execute()
nmsgs = len(tdata['messages'])
if nmsgs > 2: # skip if <3 msgs in thread
msg = tdata['messages'][0]['payload']
subject = ''
for header in msg['headers']:
if header['name'] == 'Subject':
subject = header['value']
break
if subject: # skip if no Subject line
test_response += subject + ' has num of mssgs: ' + str(nmsgs) + '<br>'
message = MIMEText('blah blah blah')
message['to'] = 'my_personal_email@gmail.com'
message['from'] = 'me'
message['subject'] = 'it seems to be working'
final_mssg = {'raw': base64.urlsafe_b64encode(message.as_string().encode()).decode()}
try:
message = (service.users().messages().send(userId='me', body=final_mssg).execute())
test_response += 'WE JUST SENT A MESSAGE WITH ID ' + message['id']
except Exception as e:
test_response += "we couldn't send the message. Error: " + str(e)
return HttpResponse(test_response)
我不知道出什么问题了。 我尝试更改代码中的许多内容,但无法摆脱此错误。 我已经为此奋斗了2天了,我快要疯了。 有人可以帮我吗?
谢谢!
PS:这是正确保存令牌的数据库条目:
天哪,我才找到解决方案。
我再次浏览了错误日志,发现变量“ client_id”是列表而不是字符串:
回到代码中,看到不应该有一个多余的逗号(因此,将“ client_id”作为列表而不是应有的字符串):
这就是引发错误的原因。 我猜它只是在需要刷新令牌时才触发它。
删除了逗号,现在可以正常使用了。 傻我 🤦
抱歉,这个问题对我来说非常具体,我想它永远也不会帮助任何人。
我知道代码需要大量重做和整理(例如,我做cred.authorize()2次!),这只是出于测试目的,我被困在其中。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.