node.js 如何在节点应用程序中解码 google OAuth 2.0 JWT(OpenID Connect)?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/20159782/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-02 16:19:56  来源:igfitidea点击:

How can I decode a google OAuth 2.0 JWT (OpenID Connect) in a node app?

node.jsexpressgoogle-oauthgoogle-openidjwt

提问by ThePuzzleMaster

I'm having a heck of a time here trying to use google OAuth to authenticate users in my node express app. I can successfully do the OAuth, which returns a response like so:

我在这里尝试使用 google OAuth 对我的 node express 应用程序中的用户进行身份验证有很长一段时间。我可以成功执行 OAuth,它返回如下响应:

{
  access_token: 'token string',
  id_token: 'id.string',
  expires_in: 3599,
  token_type: "Bearer"
}

This all makes sense, but I can't for the life of me figure out how to decode the JWT. I am a bit inexperienced in all this, so this is all a bit foreign to me.

这一切都说得通,但我终生无法弄清楚如何解码 JWT。我对这一切都缺乏经验,所以这对我来说有点陌生。

Following the instructions listed here: https://developers.google.com/accounts/docs/OAuth2Login#validatinganidtokenI am attempting to decode the JWT locally in my node app.

按照此处列出的说明进行操作:https: //developers.google.com/accounts/docs/OAuth2Login#validatinganidtoken我试图在我的节点应用程序中本地解码 JWT。

I installed https://github.com/hokaccha/node-jwt-simplein my node environment.

我在我的节点环境中安装了https://github.com/hokaccha/node-jwt-simple

And I'm pretty certain I need to use this certificate (https://www.googleapis.com/oauth2/v1/certs) in all this somehow to decode it, but I am at a bit of a loss here. I don't really understand how I get the certificate into my node app, and after that how to use it with node-jwt-simple. And I also don't really understand how I know when I need to pull a fresh certificate, vs using a cached one.

而且我很确定我需要在所有这些中使用此证书(https://www.googleapis.com/oauth2/v1/certs)以某种方式对其进行解码,但我在这里有点不知所措。我真的不明白如何将证书放入我的节点应用程序中,之后如何将它与 node-jwt-simple 一起使用。而且我也不太明白我如何知道何时需要提取新证书,而不是使用缓存的证书。

Anyone out there with some experience in this that can help me out?

任何有这方面经验的人可以帮助我吗?

Thanks for any help. I'm totally at a loss at this point.

谢谢你的帮助。在这一点上,我完全不知所措。

** Update **

** 更新 **

So I have made some progress... Kind of. By calling jwt.decode(id_token, certificate, true); I am able to successfully decode the token. Even if the certificate var is an empty object {}. This leaves me with 3 questions still. 1: What is the best way to get the certificate into my express app using the url from google? 2: How will I know when I need to pull in a fresh version of it? 3: It seems like passing in true for noVerify (3rd arg in jwt.decode) is a terrible idea. How can I get that to work without passing that in? It looks like perhaps jwt-simple is expecting hs256 and the token is using rs256.

所以我取得了一些进展......有点。通过调用 jwt.decode(id_token, certificate, true); 我能够成功解码令牌。即使证书 var 是一个空对象 {}。这给我留下了 3 个问题。1:使用谷歌的网址将证书放入我的快递应用程序的最佳方法是什么?2:我如何知道何时需要引入它的新版本?3:似乎为 noVerify 传入 true(jwt.decode 中的第三个参数)是一个糟糕的主意。我怎样才能让它工作而不传递它?看起来 jwt-simple 可能需要 hs256,而令牌正在使用 rs256。

Again, I'm super inexperienced in this, so I may be way off base here.

再说一次,我在这方面非常缺乏经验,所以我在这里可能会偏离基础。

* UPDATE *Thanks to the help from Nat, I was able to get this working! I think I tried every single JWT and JWS node module out there. What I finally landed on is as follows: I found that none of the modules that I looked at did quite what I wanted out of the box. I created the following jwt decoding helper methods that I am using to decode the id_token, so I can get the kid from the header.

* 更新 *感谢 Nat 的帮助,我能够让它正常工作!我想我尝试了那里的每个 JWT 和 JWS 节点模块。我最终发现的是:我发现我查看的所有模块都没有完全按照我的要求开箱即用。我创建了以下用于解码 id_token 的 jwt 解码辅助方法,因此我可以从标题中获取孩子。

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, '/');
}

I am using this decoding to determine if I need to pull in a new public cert from: https://www.googleapis.com/oauth2/v1/certs

我正在使用此解码来确定是否需要从以下位置获取新的公共证书:https: //www.googleapis.com/oauth2/v1/certs

Then I am using that public cert and node-jws (https://github.com/brianloveswords/node-jws) jws.verify(id_token, cert) to verify the signature!

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

Hooray! Thanks again for the extra explanation you gave in your response. That went a long way in helping me understand what I was even trying to do. Hope this might help others too.

万岁!再次感谢您在回复中提供的额外解释。这对帮助我理解我什至想要做的事情大有帮助。希望这也可以帮助其他人。

回答by Nat Sakimura

From the specification point of view, what you are encountering is [OpenID Connect].

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

id_token is a [JWS] signed [JWT]. In this case, it is a "." separated string with three components. The first portion is the header. The second is the payload. The third is the signature. Each of them are Base64url encoded string.

id_token 是一个 [JWS] 签名的 [JWT]。在这种情况下,它是一个“。” 由三个部分组成的分隔字符串。第一部分是标题。第二个是有效载荷。三是签名。它们中的每一个都是 Base64url 编码的字符串。

When you decode the header, you will get something like:

当您解码标头时,您将得到如下信息:

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

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

The "alg" indicates that the signature algorithm is RS256, which is defined in [JWA]. The "kid" indicates the key id of the public key that corresponds to the key used to sign.

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

Now I am ready to answer some of your questions:

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

2: How will I know when I need to pull in a fresh version of it?

2:我如何知道何时需要引入它的新版本?

When the kid of the cached cert file (a [JWK] file) does not match the kid in the header, fetch a new cert file. (BTW, the URL from which you pull the certs are called x5u.)

当缓存的证书文件([JWK] 文件)的孩子与标题中的孩子不匹配时,获取一个新的证书文件。(顺便说一句,您从中提取证书的 URL 称为 x5u。)

3: It seems like passing in true for noVerify (3rd arg in jwt.decode) is a terrible idea. How can I get that to work without passing that in?

3:似乎为 noVerify 传入 true(jwt.decode 中的第三个参数)是一个糟糕的主意。我怎样才能让它工作而不传递它?

Indeed. Perhaps you might want to look at another library such as kjur.github.io/jsjws/ .

的确。也许您可能想查看另一个库,例如 kjur.github.io/jsjws/ 。

References

参考

  • [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
  • [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