如何在Node.js服务器端验证Google身份验证令牌?

20

我的前端应用程序使用gmail帐户进行身份验证。

身份验证成功后,我检索id_token并将其作为bearer token发送到Authorization Header

例如: http://localhost:4000/api

Authorization Bearer token_id

nodejs服务器端,我调用以下方法来验证令牌。

exports.verifyUser = function(req, res, next) {
    var GoogleAuth = require('google-auth-library');
    var auth = new GoogleAuth();
    var client = new auth.OAuth2(config.passport.google.clientID, config.passport.google.clientSecret, config.passport.google.callbackURL);
    // check header or url parameters or post parameters for token
    var token = "";
    var tokenHeader = req.headers["authorization"];
    var items = tokenHeader.split(/[ ]+/);
    if (items.length > 1 && items[0].trim().toLowerCase() == "bearer") {
        token = items[1];
    }
    if (token) {
        var verifyToken = new Promise(function(resolve, reject) {
            client.verifyIdToken(
                token,
                config.passport.google.clientID,
                function(e, login) {
                    console.log(e);
                    if (login) {
                        var payload = login.getPayload();
                        var googleId = payload['sub'];
                        resolve(googleId);
                        next();
                    } else {
                        reject("invalid token");
                    }
                }
            )
        }).then(function(googleId) {
            res.send(googleId);
        }).catch(function(err) {
            res.send(err);
        })
    } else {
        res.send("Please pass token");
    }
}
当我调用上述方法时,总是会得到无效令牌的响应,并带有以下错误。
Error: No pem found for envelope:     {"alg":"RS256","kid":"c1ab5857066442ea01a01601
850770676460a712"}
    at OAuth2Client.verifySignedJwtWithCerts (\node_modules\google-auth-libr
ary\lib\auth\oauth2client.js:518:13)
  • 这是验证令牌的正确方法吗?
  • 我应该将id_token作为授权bearer发送吗?还是只用于授权?
  • 我应该通过url、header等方式将id_token发送到服务器端?
  • 我做错了什么?

非常感谢任何帮助。

4个回答

12

OAuth2Client.verifyIdToken 接受一个 idToken 参数,来自于 库源

/**
 * Verify id token is token by checking the certs and audience
 * @param {string} idToken ID Token.
 * @param {(string|Array.<string>)} audience The audience to verify against the ID Token
 * @param {function=} callback Callback supplying GoogleLogin if successful
 */
OAuth2Client.prototype.verifyIdToken = function(idToken, audience, callback)

您已经通过整个头部值bearer eyJhbGciOiJSUzI1NiIsImtpZCI6ImMxYWI1ODU3MDY2NDQyZWEwMWEwMTYwMTg1MDc3MDY3NjQ2MGE3MTIifQ,因此您需要将标头值拆分为:

var authorization = req.headers["authorization"];
var items = authorization.split(/[ ]+/);

if (items.length > 1 && items[0].trim() == "Bearer") {
    var token = items[1];
    console.log(token);
    // verify token
}

这是验证令牌的正确方法吗?

是的,这是验证令牌的正确方式。如果您有任何疑问或需要快速测试,还可以使用tokeninfo端点验证令牌。

https://www.googleapis.com/oauth2/v3/tokeninfo?id_token=XYZ123
  • 我应该将id_token作为授权bearer发送吗?还是只用于授权呢?
  • 我该如何将id_token发送到服务器端?通过url、header吗?

您可以将JWT令牌发送到授权头中,但这可能会导致您有多个授权头的情况。最好的方法是对令牌进行URL编码或嵌入到正文中。您可以在此处检查Google示例此处

此外,Google要求:

  • 令牌必须通过HTTPS POST发送
  • 必须验证令牌的完整性

为了优化代码,您还可以将Google auth对象移动到应用程序根目录下的app.js中,而不是每次需要验证令牌时重新定义它。在app.js中:

var app = express();

var GoogleAuth = require('google-auth-library');
var auth = new GoogleAuth();
app.authClient = new auth.OAuth2(config.passport.google.clientID, config.passport.google.clientSecret, config.passport.google.callbackURL);

verifyUser中,从req.app.authClient调用它:
req.app.authClient.verifyIdToken(...)

请你能否回答我在帖子中提出的另一个问题? - ANewGuyInTown
您说得对,由于multiple authorization header存在争议,尽管可能是可行的,但我将更新为使用URL编码或嵌入到正文中。感谢您指出这一点。 - Bertrand Martel
你不能使用GET请求,必须使用POST请求查看此链接 - Bertrand Martel
我不知道你所说的受保护API是什么意思,我只会遵循Google的建议。而且你的登录端点是为未经身份验证的用户设计的。 - Bertrand Martel
id_token不用于消费API,也许您将其与需要获取access_token以用于消费Google API的authorization_code混淆了。这个Google跨身份验证文档非常适合理解流程(它专注于Android,但同样适用于Web客户端)。 - Bertrand Martel
显示剩余9条评论

1
首先,不要使用 Id_Token 进行授权。它仅用于身份验证。请使用访问令牌进行授权。点击下方链接验证令牌。
https://www.googleapis.com/oauth2/v1/tokeninfo?access_token=${access_token}

0

今天我终于找到了答案。 Firebase工具将原生的Google与第三方登录令牌连接起来,然后封装另一层。此时获得的令牌不再是Google给我们的原始令牌。

  • A1:
    • 原始令牌: GoogleDesignInAccount Account = Task.getResult(ApiException.class);
    • Account.getidToken () // 这是原始令牌
  • B1:
    • Firebase令牌: FireBaseUser currentUser = Mauth.getCurrentUser ();
    • String token = currentUser.getIdToken(false).getResult().getToken();
  • A2:
    • Google官方提供了验证令牌的方法
  • B2:
    • Firebase官方提供了身份验证令牌的方法

我们对上述四个数据点使用代码名称。如果您需要在后台验证令牌的有效性,它们必须相互对应,A1对应A2,B1对应B2。如果您使用A2来验证B1,将会失败。


谢谢您的回答,但是您能否将图片中的内容转换为文字并在您的回答中发布,以确保即使图片被删除,答案仍然有用呢? - shaedrich

0
你现在可以简单地使用Firebase验证Google身份验证令牌。
首先,创建一个名为firebase-config的文件,并存储您从Firebase设置中获取的Firebase配置。
导入以下内容:
import { initializeApp } from 'firebase-admin/app';
import { getAuth  } from 'firebase-admin/auth';
import { firebaseConfig } from 'PATH_TO_CONFIG_FILE';  //(importing of your config file that you had created)

初始化:
const defaultApp = initializeApp(firebaseConfig);

谷歌令牌验证功能:

async verifyGoogleId(token) {
      const auth = getAuth(defaultApp);
      const firebaseUser = await auth.verifyIdToken(token);

      if (!firebaseUser) {
         throw new error();
      }
}

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