ChatGPT解决这个技术问题 Extra ChatGPT

如何识别 Google OAuth2 用户?

我使用 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/


C
Community

正如其他人所提到的,您可以使用刚刚收到的 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(节省时间)。

如评论中所述,这些令牌使用 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 我还应该指出,进行验证是绝对必要的,否则攻击者可以使用任何已注册的 Google 帐户轻松登录您的应用程序。
我不认为这完全准确。 access_token 不是 JWT 令牌,
Google 似乎已经删除了他们的 JWT 解码器,但这里有一个易于使用的解码器:jwt.io
如果您不想将您的 加密令牌 粘贴到某个 随机网站,那么您也可以通过 tr '._-' '\n/+' | sed '2s|$|===|p;d' | base64 -D 传递 id 令牌。 sed 中的 2 选择元组的第二部分,这可能是您想要的。
C
Christian

我将此方法插入到 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(注意不同的版本!)如果你不提供版本,它可能会改变?
b
blang

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

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

请参阅Google Dev OpenID Connect UserInfo


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

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


这在技术上似乎是最正确的,但它缺乏当前接受的答案所具有的额外细节。将两个答案合并在一起,你就有了金子。
O
Orange

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


h
honzajde

爪哇版

OAuth2Sample.java


关注公众号,不定期副业成功案例分享
关注公众号

不定期副业成功案例分享

领先一步获取最新的外包任务吗?

立即订阅