如何识别 Google OAuth2 用户?

Posted

技术标签:

【中文标题】如何识别 Google OAuth2 用户?【英文标题】:How to identify a Google OAuth2 user? 【发布时间】:2012-01-08 20:32:51 【问题描述】:

我使用 Facebook 登录来识别用户。当一个新用户到来时,我将他们的用户 ID 存储在我的数据库中。下次他们来的时候,我认出了他们的 Facebook ID,我知道它在我的数据库中是哪个用户。

现在我正在尝试对 Google 的 OAuth2 做同样的事情,但我如何识别用户?

Google 向我发送了几个代码和令牌(access_token、id_token、refresh_token),但它们都不是恒定的。这意味着如果我注销并在 2 分钟后重新登录,所有 3 个值都已更改。如何唯一标识用户?

我正在使用他们的 php 客户端库:https://code.google.com/p/google-api-php-client/

【问题讨论】:

【参考方案1】:

正如其他人所提到的,您可以使用刚刚收到的 OAuth2 不记名令牌向https://www.googleapis.com/oauth2/v3/userinfo 发送 GET,您将收到包含有关用户的一些信息(id、姓名等)的响应。

还值得一提的是,Google 实现了OpenID Connect,而这个用户信息端点只是其中的一部分。

OpenID Connect 是 OAuth2 之上的身份验证层。在 Google 的令牌端点交换授权 code 时,您将获得一个访问令牌(access_token 参数)以及一个 OpenID Connect ID 令牌(id_token 参数)。

这两个令牌都是 JWT(JSON Web 令牌,https://datatracker.ietf.org/doc/html/draft-ietf-oauth-json-web-token)。

如果你解码它们,你会得到一些断言,包括用户的id。如果将此 ID 链接到数据库中的用户,则可以立即识别他们,而无需执行额外的 userinfo GET(节省时间)。

如 cmets 中所述,这些令牌使用 Google 的私钥签名,您可能需要使用 Google 的公钥 (https://www.googleapis.com/oauth2/v3/certs) 验证签名以确保它们是真实的。

您可以通过将 JWT 粘贴到 https://jwt.io/(向下滚动以查看 JWT 调试器)来查看 JWT 中的内容。断言看起来像:


    "iss":"accounts.google.com",
    "id":"1625346125341653",
    "cid":"8932346534566-hoaf42fgdfgie1lm5nnl5675g7f167ovk8.apps.googleusercontent.com",
    "aud":"8932346534566-hoaf42fgdfgie1lm5nnl5675g7f167ovk8.apps.googleusercontent.com",
    "token_hash":"WQfLjdG1mDJHgJutmkjhKDCdA",
    "iat":1567923785,
    "exp":1350926995

还有用于各种编程语言的库以编程方式解码 JWT。

PS:要获取 Google 的 OpenID Connect 提供商支持的 URL 和功能的最新列表,您可以查看该 URL:https://accounts.google.com/.well-known/openid-configuration。

【讨论】:

是的,但是为了安全地使用这些信息,您必须验证 JWT 的签名,您需要公钥。知道谷歌在哪里提供他们的公钥吗? 好的,找到了:googleapis.com/oauth2/v1/certs 我还要指出,进行验证是绝对必要的,否则攻击者可以使用任何已经注册的谷歌帐户轻松登录您的应用程序。 我认为这并不完全准确。 access_token 不是 JWT 令牌, Google 似乎已经删除了他们的 JWT 解码器,但这里有一个易于使用的解码器:jwt.io 如果您不想将您的 加密令牌 粘贴到某个 随机网站,那么您也可以通过 tr '._-' '\n/+' | sed '2s|$|===|p;d' | base64 -D 传递 id 令牌。 sed 中的 2 选择元组的第二部分,这可能是您想要的。【参考方案2】:

我将此方法插入到 google-api-php-client/src/apiClient.php:

public function getUserInfo() 

    $req = new apiHttpRequest('https://www.googleapis.com/oauth2/v1/userinfo');
    // XXX error handling missing, this is just a rough draft
    $req = $this->auth->sign($req);
    $resp = $this->io->makeRequest($req)->getResponseBody();
    return json_decode($resp, 1);  

现在我可以打电话了:

$client->setAccessToken($_SESSION[ 'token' ]);
$userinfo = $client->getUserInfo();

它返回一个这样的数组(如果已请求该范围,则加上电子邮件):

Array
(
    [id] => 1045636599999999999
    [name] => Tim Strehle
    [given_name] => Tim
    [family_name] => Strehle
    [locale] => de
)

解决方案来源于这个帖子:https://groups.google.com/forum/#!msg/google-api-php-client/o1BRsQ9NvUQ/xa532MxegFIJ

【讨论】:

请注意,Google 最近更改了响应,id_token 现在在键 sub 中包含静态标识符,而不是像之前和上面示例中的键 id。 AFAIK,这种变化是他们对 OpenID Connect 协议的解释。不幸的是,当我写这篇文章时,回复似乎有些随机:有时是id,有时是sub,所以我需要同时支持两者。 我认为/oauth2/v1/userinfo 会给你id/oauth2/v3/userinfo 会给你sub(注意不同的版本!)如果你不提供版本可能会改变? 【参考方案3】:

应该提到的是,OpenID Connect API 不再返回 id 属性。

现在是 sub 属性,用作唯一的用户标识。

见Google Dev OpenID Connect UserInfo

【讨论】:

不确定这是否正确。尽管文档说明了什么,但如果我重新启动我的身份验证服务器,清除缓存并重新登录,“子”是不同的。 有没有其他人遇到过 HockeyJ 的问题,即在同一登录时更改“sub”? 是的,我有同样的问题,sub 不同。刚刚在另一个线程中问了这个问题***.com/questions/53421907/…【参考方案4】:

“这是谁?”本质上是一种服务;您必须以 范围 的形式请求访问它,然后向 Google 配置文件 资源服务器 发出请求以获取身份。详情请见OAuth 2.0 for Login。

【讨论】:

这在技术上似乎是最正确的,但它缺少当前接受的答案所具有的额外细节。将这两个答案合并在一起,你就有了金子。【参考方案5】:

可以使用公钥在本地验证 JWT,(Google API 客户端库会自动下载并缓存它们的公钥)通过https://www.googleapis.com/oauth2/v1/tokeninfo 端点检查 Google 端的令牌是必要的,以检查应用程序的访问权限是否具有自创建令牌以来已被撤销。

【讨论】:

【参考方案6】:

Java 版本

OAuth2Sample.java

【讨论】:

以上是关于如何识别 Google OAuth2 用户?的主要内容,如果未能解决你的问题,请参考以下文章

如何获取经过 Google 身份验证的用户(oauth2)的 Google 聊天(环聊)ID?

如何使用 Spring Boot + Angular 处理外部 OAuth2 身份验证

使用 Spring Boot OAuth2 针对 Google 进行身份验证时如何将 google 用户电子邮件发送到白名单用户

Google Adwords API with OAuth2 如何获取授权用户的电子邮件?

如何将 Google openidclaim_id 与 oauth2 用户 ID 连接起来

低代码如何构建支持OAuth2.0的后端Web API