Google Data API:如何对桌面应用程序进行身份验证

发布于 2024-10-17 00:28:07 字数 5187 浏览 7 评论 0原文

我想知道在桌面应用程序中对 Google Data API 用户进行身份验证的最佳/最简单方法。

我通读了文档,看来我的选项是 ClientLogin 或 OAuth。

对于 ClientLogin,似乎我必须自己实现登录/密码的 UI(以及相关的东西,例如将其保存在某处等)。我真的想知道是否有更多的支持,可能会弹出一些默认的登录/密码屏幕并使用操作系统钥匙串来存储密码等。我想知道为什么不存在这样的支持?这不是标准程序吗?通过将该实现留给开发人员(好吧,将该实现留给开发人员当然是件好事),我猜想很多人在这里想出了非常丑陋的解决方案(当他们只想编写一个小脚本时) )。

OAuth 似乎是更好的解决方案。然而,似乎缺少一些代码和/或我发现的大多数代码似乎只与 Web 应用程序相关。特别是,我按照文档操作并得到了 here 。在简介中,它已经谈到了 Web 应用程序。然后,我需要指定一个对于桌面应用程序没有意义的回调 URL。另外我想知道我应该输入什么消费者密钥/秘密,因为这对于桌面应用程序来说也没有真正意义(尤其是对于开源应用程序来说)。我搜索了一下,据说 这里(关于SO)我应该使用“匿名”/“匿名”作为消费者密钥/秘密;但是 Google 文档中哪里提到了这一点呢?用户身份验证后如何获取令牌?

有一些示例代码吗? (不是使用硬编码的用户名/密码,而是使用可重复使用的完整身份验证方法。)

谢谢, 阿尔伯特


到目前为止我的代码:

import gdata.gauth
import gdata.contacts.client

CONSUMER_KEY = 'anonymous'
CONSUMER_SECRET = 'anonymous'
SCOPES = [ "https://www.google.com/m8/feeds/" ] # contacts

client = gdata.contacts.client.ContactsClient(source='Test app')

import BaseHTTPServer
import SocketServer

Handler = BaseHTTPServer.BaseHTTPRequestHandler
httpd = BaseHTTPServer.HTTPServer(("", 0), Handler)
_,port = httpd.server_address

oauth_callback_url = 'http://localhost:%d/get_access_token' % port
request_token = client.GetOAuthToken(
    SCOPES, oauth_callback_url, CONSUMER_KEY, consumer_secret=CONSUMER_SECRET)

loginurl = request_token.generate_authorization_url(google_apps_domain=None)
loginurl = str(loginurl)
import webbrowser
webbrowser.open(loginurl)

但是,这不起作用。我收到此错误:

抱歉,您访问的域的登录页面未使用 Google Apps。请检查网址并重试。

我不太明白这一点。当然,我不使用 Google Apps。


啊,该错误来自 generate_authorization_url 中的 google_apps_domain=None。离开它(即只是 loginurl = request_token.generate_authorization_url() ,它到目前为止有效。

我当前的代码:

import gdata.gauth
import gdata.contacts.client

CONSUMER_KEY = 'anonymous'
CONSUMER_SECRET = 'anonymous'
SCOPES = [ "https://www.google.com/m8/feeds/" ] # contacts

client = gdata.contacts.client.ContactsClient(source='Test app')

import BaseHTTPServer
import SocketServer

httpd_access_token_callback = None
class Handler(BaseHTTPServer.BaseHTTPRequestHandler):
    def do_GET(self):
        if self.path.startswith("/get_access_token?"):
            global httpd_access_token_callback
            httpd_access_token_callback = self.path
        self.send_response(200)
    def log_message(self, format, *args): pass
httpd = BaseHTTPServer.HTTPServer(("", 0), Handler)
_,port = httpd.server_address

oauth_callback_url = 'http://localhost:%d/get_access_token' % port
request_token = client.GetOAuthToken(
    SCOPES, oauth_callback_url, CONSUMER_KEY, consumer_secret=CONSUMER_SECRET)

loginurl = request_token.generate_authorization_url()
loginurl = str(loginurl)
print "opening oauth login page ..."
import webbrowser; webbrowser.open(loginurl)

print "waiting for redirect callback ..."
while httpd_access_token_callback == None:
    httpd.handle_request()

print "done"

request_token = gdata.gauth.AuthorizeRequestToken(request_token, httpd_access_token_callback)

# Upgrade the token and save in the user's datastore
access_token = client.GetAccessToken(request_token)
client.auth_token = access_token

这将打开 Google OAuth 页面,底部有提示:

该网站尚未向 Google 注册,无法为授权请求建立安全连接。我们建议您拒绝访问,除非您信任该网站。

但它仍然不起作用。当我尝试访问联系人(即只是一个 client.GetContacts())时,我收到此错误:

gdata.client.Unauthorized: Unauthorized - Server responded with: 401, <HTML>
<HEAD>
<TITLE>Token invalid - AuthSub token has wrong scope</TITLE>
</HEAD>
<BODY BGCOLOR="#FFFFFF" TEXT="#000000">
<H1>Token invalid - AuthSub token has wrong scope</H1>
<H2>Error 401</H2>
</BODY>
</HTML>

好的,看来我确实设置了错误的范围。当我使用 http 而不是 https 时(即 SCOPES = [ "http://www.google.com/m8/feeds/" ] ), 有用。

但我真的很想使用https。我想知道我怎样才能做到这一点。


另外,此解决方案的另一个问题:

在我的 Google 帐户的授权访问列表中,我现在有一堆这样的本地主机条目:

localhost:58630 — Google 通讯录 [ 撤销访问权限 ]
本地主机:58559 — Google 通讯录 [ 撤销访问权限 ]
本地主机:58815 — Google 通讯录 [ 撤销访问权限 ]
本地主机:59174 — Google 通讯录 [ 撤销访问权限 ]
本地主机:58514 — Google 通讯录 [ 撤销访问权限 ]
本地主机:58533 — Google 通讯录 [ 撤销访问权限 ]
本地主机:58790 — Google 通讯录 [ 撤销访问权限 ]
本地主机:59012 — Google 通讯录 [ 撤销访问权限 ]
本地主机:59191 — Google 通讯录 [撤销访问权限]

我想知道如何避免它会进行此类输入。

当我使用 xoauth_displayname 时,它​​会显示该名称,但仍然会生成多个条目(可能是因为每次 URL 仍然大部分不同(因为端口))。我怎样才能避免这种情况?


我当前的代码现已位于 Github 上。


我还想知道我应该在哪里、如何以及多长时间存储访问令牌和/或请求令牌,以便用户每次启动应用程序时都不会一次又一次地询问用户。

I wonder about the best/easiest way to authenticate the user for the Google Data API in a desktop app.

I read through the docs and it seems that my options are ClientLogin or OAuth.

For ClientLogin, it seems I have to implement the UI for login/password (and related things like saving this somewhere etc.) myself. I really wonder if there is some more support there which may pop up some default login/password screen and uses the OS keychain to store the password, etc. I wonder why such support isn't there? Wouldn't that be the standard procedure? By leaving that implementation to the dev (well, the possibility to leave that impl to the dev is good of course), I would guess that many people have come up with very ugly solutions here (when they just wanted to hack together a small script).

OAuth seems to be the better solution. However, there seems to be some code missing and/or most code I found seems only to be relevant for web applications. Esp., I followed the documentation and got here. Already in the introduction, it speaks about web application. Then later on, I need to specify a callback URL which does not make sense for a desktop application. Also I wonder what consumer key/secret I should put as that also doesn't really make sense for a desktop app (esp. not for an open-source one). I searched a bit around and it was said here (on SO) that I should use "anonymous"/"anonymous" as the consumer key/secret; but where does it say that in the Google documentation? And how do I get the token after the user has authenticated itself?

Is there some sample code? (Not with a hardcoded username/password but with a reusable full authentication method.)

Thanks,
Albert


My code so far:

import gdata.gauth
import gdata.contacts.client

CONSUMER_KEY = 'anonymous'
CONSUMER_SECRET = 'anonymous'
SCOPES = [ "https://www.google.com/m8/feeds/" ] # contacts

client = gdata.contacts.client.ContactsClient(source='Test app')

import BaseHTTPServer
import SocketServer

Handler = BaseHTTPServer.BaseHTTPRequestHandler
httpd = BaseHTTPServer.HTTPServer(("", 0), Handler)
_,port = httpd.server_address

oauth_callback_url = 'http://localhost:%d/get_access_token' % port
request_token = client.GetOAuthToken(
    SCOPES, oauth_callback_url, CONSUMER_KEY, consumer_secret=CONSUMER_SECRET)

loginurl = request_token.generate_authorization_url(google_apps_domain=None)
loginurl = str(loginurl)
import webbrowser
webbrowser.open(loginurl)

However, this does not work. I get this error:

Sorry, you've reached a login page for a domain that isn't using Google Apps. Please check the web address and try again.

I don't quite understand that. Of course I don't use Google Apps.


Ah, that error came from google_apps_domain=None in generate_authorization_url. Leave that away (i.e. just loginurl = request_token.generate_authorization_url() and it works so far.

My current code:

import gdata.gauth
import gdata.contacts.client

CONSUMER_KEY = 'anonymous'
CONSUMER_SECRET = 'anonymous'
SCOPES = [ "https://www.google.com/m8/feeds/" ] # contacts

client = gdata.contacts.client.ContactsClient(source='Test app')

import BaseHTTPServer
import SocketServer

httpd_access_token_callback = None
class Handler(BaseHTTPServer.BaseHTTPRequestHandler):
    def do_GET(self):
        if self.path.startswith("/get_access_token?"):
            global httpd_access_token_callback
            httpd_access_token_callback = self.path
        self.send_response(200)
    def log_message(self, format, *args): pass
httpd = BaseHTTPServer.HTTPServer(("", 0), Handler)
_,port = httpd.server_address

oauth_callback_url = 'http://localhost:%d/get_access_token' % port
request_token = client.GetOAuthToken(
    SCOPES, oauth_callback_url, CONSUMER_KEY, consumer_secret=CONSUMER_SECRET)

loginurl = request_token.generate_authorization_url()
loginurl = str(loginurl)
print "opening oauth login page ..."
import webbrowser; webbrowser.open(loginurl)

print "waiting for redirect callback ..."
while httpd_access_token_callback == None:
    httpd.handle_request()

print "done"

request_token = gdata.gauth.AuthorizeRequestToken(request_token, httpd_access_token_callback)

# Upgrade the token and save in the user's datastore
access_token = client.GetAccessToken(request_token)
client.auth_token = access_token

That will open the Google OAuth page with the hint at the bottom:

This website has not registered with Google to establish a secure connection for authorization requests. We recommend that you deny access unless you trust the website.

It still doesn't work, though. When I try to access the contacts (i.e. just a client.GetContacts()), I get this error:

gdata.client.Unauthorized: Unauthorized - Server responded with: 401, <HTML>
<HEAD>
<TITLE>Token invalid - AuthSub token has wrong scope</TITLE>
</HEAD>
<BODY BGCOLOR="#FFFFFF" TEXT="#000000">
<H1>Token invalid - AuthSub token has wrong scope</H1>
<H2>Error 401</H2>
</BODY>
</HTML>

Ok, it seems that I really had set the wrong scope. When I use http instead of https (i.e. SCOPES = [ "http://www.google.com/m8/feeds/" ]), it works.

But I really would like to use https. I wonder how I can do that.


Also, another problem with this solution:

In the list of Authorized Access to my Google Account, I now have a bunch of such localhost entries:

localhost:58630 — Google Contacts [ Revoke Access ]
localhost:58559 — Google Contacts [ Revoke Access ]
localhost:58815 — Google Contacts [ Revoke Access ]
localhost:59174 — Google Contacts [ Revoke Access ]
localhost:58514 — Google Contacts [ Revoke Access ]
localhost:58533 — Google Contacts [ Revoke Access ]
localhost:58790 — Google Contacts [ Revoke Access ]
localhost:59012 — Google Contacts [ Revoke Access ]
localhost:59191 — Google Contacts [ Revoke Access ]

I wonder how I can avoid that it will make such entries.

When I use xoauth_displayname, it displays that name instead but still makes multiple entries (probably because the URL is still mostly different (because of the port) each time). How can I avoid that?


My current code is now on Github.


I also wonder, where, how and for how long I should store the access token and/or the request token so that the user is not asked always again and again each time when the user starts the application.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

尘曦 2024-10-24 00:28:07

OAuth 也可以在桌面应用程序中使用。消费者密钥和秘密“匿名”非常适合此类应用程序。

用户不会验证自己的身份。您将从提供商 (Google) 获得一个令牌,然后用户将授权该令牌作为受信任的消费者(您的应用程序)的令牌,通过该令牌可以访问和使用他们的 Google 服务。

这是一个很好的 OAuth python 库:

https://github.com/simplegeo/python-oauth2

以下是 OAuth 工作原理的概述:

http://blog.doityourselfandroid。 com/2010/11/07/google-oauth-overview/

下面是一个 Java 示例,其中还解释了 OAuth 身份验证要采取的步骤:

http://nilvec.com/implementing-client-side-oauth-on-android/

HTH。

OAuth can be used in desktop apps as well. The consumer key and secret 'anonymous' is perfectly suitable for such an app.

The user won't authenticate themselves. You will get a token from the provider (Google) and then this will be authorized by the user as a token for a trusted consumer (your app), with which their Google service(s) can be accessed and used.

Here's a good python library for OAuth:

https://github.com/simplegeo/python-oauth2

Here's an overview on how OAuth works:

http://blog.doityourselfandroid.com/2010/11/07/google-oauth-overview/

Here's an example for Java which also explains the steps to be taken for OAuth authentication:

http://nilvec.com/implementing-client-side-oauth-on-android/

HTH.

沉默的熊 2024-10-24 00:28:07

这是您可以通过桌面应用程序从 Google 进行身份验证的方式。
(我就是这样做的)
https://developers.google.com/accounts/docs/OAuth2InstalledApp
https://developers.google.com/accounts/docs/OAuth2Login

测试:
https://www.googleapis.com/oauth2/v1/userinfo?access_token=ACCESS_TOKEN

This is how you can authenticate from Google via your desktop application.
(This is how I did it)
https://developers.google.com/accounts/docs/OAuth2InstalledApp
https://developers.google.com/accounts/docs/OAuth2Login

Test:
https://www.googleapis.com/oauth2/v1/userinfo?access_token=ACCESS_TOKEN

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文