如何在Node应用中解码Google OAuth 2.0 JWT(OpenID Connect)?

49

我在这里尝试在我的Node Express应用程序中使用Google OAuth来验证用户,但是遇到了很大的问题。我可以成功地进行OAuth,返回一个类似于下面这样的响应:

{
  access_token: 'token string',
  id_token: 'id.string',
  expires_in: 3599,
  token_type: "Bearer"
}
这都有道理,但我怎么也想不出如何解码JWT。我在这方面有些经验不足,所以这对我来说都有点陌生。按照此处列出的说明:https://developers.google.com/accounts/docs/OAuth2Login#validatinganidtoken,我正在尝试在我的Node应用程序中本地解码JWT。我在我的Node环境中安装了https://github.com/hokaccha/node-jwt-simple。我非常确定我需要在所有这些操作中使用此证书(https://www.googleapis.com/oauth2/v1/certs)来解码它,但是我在这里有点迷失了。我真的不明白如何将证书导入我的Node应用程序,然后如何与node-jwt-simple一起使用它。我也不太明白如何知道何时需要拉取新证书,而不是使用已缓存的证书。有没有什么人有相关经验可以帮帮我?感谢任何帮助,我完全迷失了。

*更新*
多亏了Nat的帮助,我终于搞定了!我觉得我尝试了每个可用的JWT和JWS节点模块。我最终采用的是以下方法:我发现我看过的所有模块都不能完全满足我的需求。我创建了下面这些jwt解码帮助方法,用于解码id_token,以便我可以从标头中获取kid。

module.exports = {
  decodeJwt: function (token) {
    var segments = token.split('.');

    if (segments.length !== 3) {
      throw new Error('Not enough or too many segments');
    }

    // All segment should be base64
    var headerSeg = segments[0];
    var payloadSeg = segments[1];
    var signatureSeg = segments[2];

    // base64 decode and parse JSON
    var header = JSON.parse(base64urlDecode(headerSeg));
    var payload = JSON.parse(base64urlDecode(payloadSeg));

    return {
      header: header,
      payload: payload,
      signature: signatureSeg
    }

  }
}

function base64urlDecode(str) {
  return new Buffer(base64urlUnescape(str), 'base64').toString();
};

function base64urlUnescape(str) {
  str += Array(5 - str.length % 4).join('=');
  return str.replace(/\-/g, '+').replace(/_/g, '/');
}

我使用这个解码来确定是否需要从以下网址获取新的公共证书:https://www.googleapis.com/oauth2/v1/certs

然后,我使用该公共证书和node-jws (https://github.com/brianloveswords/node-jws)的 jws.verify(id_token, cert) 来验证签名!

太好了!感谢您在回复中提供的额外解释。对我理解自己正在尝试做什么非常有帮助。希望这也可以帮助其他人。

1个回答

63

从规范的角度来看,您遇到的是[OpenID Connect]。

id_token是一个[JWS]签名的[JWT]。在这种情况下,它是由三个部分组成的点(.)分隔字符串。第一部分是头信息,第二部分是载荷,第三部分是签名。每个部分都是Base64url编码字符串。

解码头信息后,您会得到类似于:

{"alg":"RS256","kid":"43ebb53b0397e7aaf3087d6844e37d55c5fb1b67"}

"alg"表示签名算法为RS256,定义在[JWA]中。"kid"表示公钥的密钥ID与用于签名的密钥相对应。

现在我准备回答你一些问题:

2:什么时候需要拉取最新版本?

当缓存证书文件([JWK]文件)的kid与头信息中的kid不匹配时,请获取新的证书文件。(顺便说一句,拉取证书的URL称为x5u。)

3:似乎传入true作为jwt.decode的第三个参数noVerify是一个糟糕的主意。如何在不传递该参数的情况下使其工作?

确实如此。也许您想看看另一个库,比如kjur.github.io/jsjws/。

参考资料

  • [OpenID Connect] openid.bitbucket.org/openid-connect-core-1_0.html
  • [JWS] tools.ietf.org/html/draft-ietf-jose-json-web-signature
  • [JWT] tools.ietf.org/html/draft-ietf-oauth-json-web-token‎
  • [JWK] tools.ietf.org/html/draft-ietf-oauth-json-web-keys
  • [JWA] tools.ietf.org/html/draft-ietf-jose-json-web-algorithms

太棒了!非常感谢您详尽而详细的解释。这对我理解我所要做的事情非常有帮助。现在,如果我有任何问题,我知道应该搜索什么了。我真的很感激。 - ThePuzzleMaster
你说“当你解码标题时”,但我想知道:是否有一种简单的方法可以在前端仅使用JavaScript “解码” id_token - mesqueeb
很棒的答案!我也发现这篇文章非常有趣:https://darutk.medium.com/understanding-id-token-5f83f50fa02e - Franco Rondini
移动端的流程是什么?谷歌会提供idToken和其他基本信息。用户将使用该电子邮件等信息调用我的Node.js API以获取JWT令牌。在该API中,我如何验证此调用来自真实用户?我不能仅允许用户使用电子邮件登录,也无法验证idToken,因为这只是三个Base64对象的组合信息。 - Sunil Garg

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