使用OAuth2为应用程序和网站进行身份验证

68

我正在开发一个主要通过应用访问的网站,并且我想使用OAuth2进行用户注册和认证。由于这是一个Android应用程序,因此我将开始使用Google的OAuth2,因为它在Android上提供了良好的UI界面。

Google表示"您可以选择使用Google的身份验证系统作为外包用户身份验证的一种方式。这可以消除创建、维护和保护用户名和密码存储的需要。" 这正是我想做的事情。然而当我浏览他们所有的示例等内容时,我只能找到关于网站或应用程序对Google服务进行用户认证的内容。

实际上,当我注册我的应用程序(“客户端”)时,Google的OAuth2有网站客户端和“已安装”客户端(即移动应用程序)的选项,但两者不能同时存在。我可以创建两个单独的客户端,但我读了OAuth2草案,我认为会有问题,现在我将解释一下。

以下是我设想的工作方式:

OAuth2 flow diagram


  1. 用户请求MyApp访问他的私人数据。
  2. 应用程序使用Android的AccountManager类请求Google API的访问令牌。
  3. Android向用户询问:“应用程序'MyApp'想要访问您在Google上的基本信息,是否允许?”
  4. 用户回答是。
  5. AccountManager使用存储在手机上的凭据连接到Google的OAuth2服务器,并请求访问令牌。
  6. 访问令牌(遵循绿线)被返回。
  7. AccountManager将访问令牌返回给MyApp。
  8. MyApp发送一个包含访问令牌的请求到MySite以获取用户的私人数据。
  9. MySite需要使用访问令牌验证用户。它使用此处描述中所述的方法与Google验证令牌 - “Google,这个令牌有效吗?”。
  10. 现在,我希望发生的是Google说:“是的,无论谁给了你这个令牌,他确实是那个用户。”,但我认为实际上会发生的事情(根据OAuth2草案和Google的文档)是它会说:“不行!该令牌仅对MyApp有效,而你是MySite。滚开!”。
所以我应该怎么做呢?请不要说“使用OpenID”或“不要使用OAuth2”或其他类似没用的答案。哦,我真的很想继续使用漂亮的AccountManager UI而不是糟糕的弹出式WebView

编辑

Nikolay的临时答案(如果有效,我会回报!)是它实际上应该有效,Google的服务器不会关心访问令牌来自哪里。对我来说似乎有点不安全,但我会看看它是否有效!

更新

我使用Facebook而不是Google实现了这种模式,完全可行。 OAuth2服务器不关心访问令牌来自何处。至少Facebook不会,所以我认为Google也不会。

考虑到将访问令牌存储起来是非常不好的想法!但我们也不希望为了检查每个请求的身份验证而不得不访问Facebook/Google的服务器,因为这会减慢所有操作。最好的方法可能是为您的站点添加一个额外的身份验证cookie,当其访问令牌被验证时,您会分发该cookie,但更简单的方法就是像处理密码一样处理访问令牌,并存储其哈希值。您不需要加盐,因为访问令牌非常长。因此,上述步骤变成了以下类似形式:
9. MySite需要使用访问令牌验证用户。首先,它会检查其缓存的哈希有效访问令牌。如果在那里找到令牌的哈希,则知道用户已通过身份验证。否则,它会按照在此处描述与Google进行检查 - “Google,这个令牌是否有效?”。
10. 如果Google表示访问令牌无效,则告诉用户离开。否则,Google会说“是的,这是一个有效的用户”,然后我们会检查我们的注册用户数据库。如果未找到该Google用户名(或者如果使用Facebook,则为Facebook ID),则可以创建新用户。然后,我们会缓存访问令牌的哈希值。

好奇 - 最终你采用的上述方法行得通吗? - Mathias Conradt
我还没有实际实现它,抱歉! - Timmmm
1
请参见更新的问题:至少在Facebook上它起作用了。 - Timmmm
3
我也是用这种方式实现的,它很有效。在安卓上使用Google API、Google Play Services,并在我的Spring3应用程序中重新使用服务器端的token。 - Mathias Conradt
10
非常感谢清晰易懂的问题和有用的图表,我会为您翻译。您希望再给出一个赞,以表达对分享结果的感激之情。 - sven
2
谢谢。我没有加上一个更新:Facebook实际上建议将访问令牌存储在数据库中,因为您需要它们才能在Facebook服务器上执行操作(假设您想要执行)。这可能并不像存储密码那样糟糕,因为如果数据库被攻击(并且您意识到了),则可以使所有泄露的访问令牌无效。 - Timmmm
6个回答

5

我刚刚在类似的 StackOverflow 问题中发布了一个答案

谷歌称之为混合应用程序,并解释了如何使一个“Android 应用程序获得 Web 后端的离线访问”

要点是您需要将一个经过调整的scope字符串传递到GoogleAuthUtil.getToken中,以便它返回授权代码(而不是 OAuth2 令牌)。根据此示意图,该授权代码可以从您的移动应用程序传递到您的服务器,并交换为 OAuth2 令牌和刷新令牌。

scope参数需要看起来像这样:

oauth2:server:client_id:<your_server_client_it>:api_scope:<scope_url_1> <scope_url_2> ...

1
嘿@Wolfram Arnold,这个以前对我有用,但是仅仅几天前它就停止工作了,现在它抛出了这个错误com.google.android.gms.auth.GoogleAuthException: Unknown. 你知道为什么吗?我的范围有问题吗?https://gist.github.com/lawloretienne/7351151 - Etienne Lawlor
抱歉,我不确定。我已经有一段时间没有尝试过了。Google可能改变了什么 :-( - Wolfram Arnold

2

2

1

你可能需要使用OpenID Connect,它使用OAuth令牌进行身份验证。至于AccountManager,当前的OAuth支持有点不太完善,即将发布的Google Play Services应该会使其更好。请参见此处的演示


1
我看不出那个 OpenID Connect 演示与我原本计划使用的 OAuth2 有什么不同。我的意思是它根本没有提到 OpenID... - Timmmm
1
没有在实际项目中使用过,但是是的。此 Token 用于认证/用户信息服务,所以没有人应该会抱怨 :) - Nikolay Elenkov
刚开始熟悉Google Play服务,想知道为什么文档只列出了Google+范围,而其他所有范围基本上也可以工作。我猜这份文档还在不断完善中。 - Mathias Conradt
Google终于发布了一个非常好的指南,用于使用Play服务来实现这个功能:验证Android应用程序的后端调用 - ejegg
嘿@Nikolay,这个以前对我有用,但是仅仅几天前它就停止工作了,现在它抛出了这个错误com.google.android.gms.auth.GoogleAuthException: Unknown。你知道为什么吗?gist.github.com/lawloretienne/7351151 - Etienne Lawlor
显示剩余6条评论

1
至少在谷歌上,访问令牌最终会过期。这就是为什么Android AccountManagerinvalidateAuthToken方法--缓存的访问令牌已过期,您需要告诉AccountManager停止提供旧的令牌,而是获取一个新的令牌。这使得缓存令牌更加安全,因为令牌本身并不会给您永久访问该用户的权限。相反,当有效时,它仅表示“在最近的某个时间点,此令牌是由可信源获取的”。

以下是我在处理令牌时发现有用的几个事项。首先是Google的tokeninfo端点。令牌本身只是base64编码的JSON。这意味着它没有加密,因此您需要确保使用HTTPS进行通信。但是,这也意味着您可以检查令牌并更好地了解正在发生的情况。

https://www.googleapis.com/oauth2/v1/tokeninfo?id_token=

如果您的令牌为“abcdef”,则需要导航到:

https://www.googleapis.com/oauth2/v1/tokeninfo?id_token=abcdef

而Google将为您解析令牌。这是一个简单的JSON对象,其中包括一个“expires_in”字段,告诉您令牌仍然有效的秒数。在下面的视频中的6:03,您可以看到已解析的令牌:

https://developers.google.com/events/io/sessions/383266187

那个视频包含了OAuth2的全面概述,如果你要处理OAuth和令牌,那么完整地观看它是非常值得的。演讲者还讨论了其他形式的OAuth2令牌,这些令牌不是访问令牌,也不会过期。
另一个有用的资源是OAuth Playground。这使您可以执行基本操作,如请求范围、制作请求并获取令牌。此链接似乎间歇性工作,在Chrome上,我必须安装Oauth Playground应用程序。

https://developers.google.com/oauthplayground/

以下是需要翻译的内容:

这里有一篇由视频演讲者Tim Bray编写的教程,解释如何使用访问令牌从Android应用程序与服务器通信。这对我很有帮助,因为我开始理解Google API控制台中不同组件如何协同工作:

http://android-developers.blogspot.in/2013/01/verifying-back-end-calls-from-android.html

关于您的问题的实际答案,我会说您永远不需要在服务器上缓存访问令牌。如上面的“从Android验证后端调用”链接中所解释的那样,验证令牌几乎总是一个快速静态调用,这意味着没有理由缓存令牌:

库可以缓存Google证书,并仅在需要时刷新它们,因此验证(几乎总是)是一个快速静态调用。

最后,您确实可以使用AccountManager获取访问令牌。但是,Google现在鼓励使用Play Services库中的GoogleAuthUtil类:

简而言之,使用OAuth2请求getAuthToken和getToken有什么区别?

注意Tim Bray在上面链接中的评论,他表示他们正在努力推进GoogleAuthUtil路线。但是请注意,这意味着您将受到Google身份验证的限制。我相信AccountManager可以用于获取例如Facebook令牌等,而GoogleAuthUtil则不行。

嘿 @user1978019,这个对我以前可行,但几天前就不行了,现在抛出了这个错误 com.google.android.gms.auth.GoogleAuthException: Unknown. 你有任何想法吗?我的范围有什么问题吗?https://gist.github.com/lawloretienne/7351151 - Etienne Lawlor
抱歉,toobsco42,刚看到这个消息。你解决了吗? - user1978019

0
当我们需要在非 Google OAuth 服务器上执行类似操作时,我们会将令牌保存在网站的数据库中。然后应用程序将使用 web 服务在需要请求数据时请求令牌。
用户可以在 web 或 app 上进行 OAuth 注册。他们共享同一应用程序令牌,因此可以共享访问令牌。注册后,我们将存储访问和刷新令牌在 DB 中供任何需要它的应用程序使用。

你所说的“应用程序令牌”是指“客户端ID”吗?因为据我所知,没有办法让“AccountManager”使用与我的网站相同的客户端ID。另外,当应用程序请求用户的访问令牌时,如何对其进行身份验证?谢谢。 - Timmmm
这是几年前发生的事情,当时我们使用的是OAuth 1.0。该系统被设计为用户可以通过网站或移动应用程序进行身份验证,同时在两者中使用相同的应用程序令牌。身份验证后,访问令牌存储在网站后面的数据库中。每当用户登录应用程序(Web或应用)时,都会向数据库发出请求(通过应用程序的Web服务),以获取访问令牌。如果没有访问令牌,则会采用注册流程。至于身份验证Web服务,我们在SOAP Web服务上使用了WS-Security。 - Mark S.
也许我误解了,但是我的流氓应用要如何阻止向你的 Web 服务请求他人的访问令牌呢?无论如何,它听起来与我要做的大致相似,只是我猜现在由于客户端 ID 的原因,使用 OAuth2 不可能了。我唯一能想到的就是让我的站点冒充这个应用程序。不过这似乎很 hacky。 - Timmmm
您需要拥有我的WS-Security Web服务头的证书。没有该证书,您将无法调用我的Web服务。 - Mark S.
1
听起来不太安全!我可以问一下这个应用程序/网站的名称吗?你知道,为了研究... - Timmmm
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接