我遵循了这个Gmail API Python快速入门教程: https://developers.google.com/gmail/api/quickstart/python
我按照说明在API控制台中配置了我的OAuth客户端ID(请参阅下面的第一张图片)。但是,启动该脚本会打开一个浏览器会话,导致下面的400错误。
重定向网址与API控制台中注册的内容相匹配。
但是,快速入门脚本会打开以下URL: https://accounts.google.com/o/oauth2 ...& redirect_uri = http%3A%2F%2Flocalhost%3A8080%2F ...
当我继续执行授权请求时,手动将重定向URI更改为http://localhost:8080可以部分解决问题,但是响应无法返回到命令提示符。
如何强制快速启动程序生成一个将重定向URI保留为 http://localhost:8080 的网址?
答案 0 :(得分:3)
您遇到错误的原因是Python Quickstart说:
<强> d 即可。选择“凭据”选项卡,单击“创建凭据”按钮,然后选择“OAuth客户端ID”。
<强>电子即可。选择应用类型其他,输入名称“Gmail API快速入门”,然后点击“创建”按钮。
但是,看看你在做什么,你正在使用 Web应用程序而不是其他。
当我使用其他作为client_secret.json时,我没有遇到这个问题。
<强>结果强>:
身份验证流程已完成。
不需要黑客/解决方法。只需按照说明操作:)
答案 1 :(得分:0)
<强>解释强>
这种情况正在发生,因为传递给Google API服务器的 uri_redirect 参数是一个百分比编码的ASCII字符串。这可以通过查看脚本启动的URL来验证:
...redirect_uri=http%3A%2F%2Flocalhost%3A8080%2F...
在执行此行之后的步骤中,整个URL由快速入门脚本编码:
credentials = tools.run_flow(flow, store, flags)
使用调试器逐步执行该过程会发现URL最终使用Python的urllib库中的urlencode方法进行编码。这会导致client_secret.json文件中的redirect_uri参数从:
转换http://localhost:8080
要
http%3A%2F%2Flocalhost%3A8080%2F
当Google收到OAuth请求时,会将编码 uri_redirect参数与API控制台中注册的未编码参数进行比较。由于它们不匹配,因此返回redirect_uri_mismatch。
<强>解决方案强>
理想情况下,Google应修改API端点,以确保在必要时对此参数进行解码,然后再将其与API控制台中注册的参数进行比较。
如果API控制台接受编码的重定向URI条目,则可接受的修复方法是,但它不会:
解决方法(警告:hacky)
只需在oauth2client库中的两个位置替换编码的redirect_uri参数:
(1)<{3}}中的 update_query_params 函数
...
start = new_query.find("redirect_uri")+13
end = new_query.find("&",start)
new_query2 = new_query[:start] + "http://localhost:8080" + new_query[end:]
new_parts = parts._replace(query=new_query2)
...
(2)_helpers.py
中的 step2_exchange...
body = urllib.parse.urlencode(post_data)
start = body.find("redirect_uri")+13
end = body.find("&",start)
body2 = body[:start] + "http://localhost:8080" + body[end:]
headers = {
'content-type': 'application/x-www-form-urlencoded',
}
if self.authorization_header is not None:
headers['Authorization'] = self.authorization_header
if self.user_agent is not None:
headers['user-agent'] = self.user_agent
if http is None:
http = transport.get_http_object()
resp, content = transport.request(
http, self.token_uri, method='POST', body=body2, headers=headers)
...
现在一切正常。