4

我正在使用Google 的 OpenIDConnect 身份验证,并且我想验证id_token从 Google 返回的 JWT。iss但是,关于 Google 为ID 令牌中的(颁发者)声明返回的值,文档似乎不一致。

一个页面显示iss: always accounts.google.com”,但另一个页面显示“ID 令牌中 iss 的值等于accounts.google.comhttps://accounts.google.com”,示例代码中的注释进一步说明:

// If you retrieved the token on Android using the Play Services 8.3 API or newer, set
// the issuer to "https://accounts.google.com". Otherwise, set the issuer to
// "accounts.google.com". If you need to verify tokens from multiple sources, build
// a GoogleIdTokenVerifier for each issuer and try them both.

我有一个服务器端应用程序,而不是 Android 应用程序,所以我没有使用 Play Services。

为了进一步搅浑水,OpenIDConnect 规范本身包含一个注释:

实施者可能想知道,在撰写本文时,Google 部署的 OpenID Connect 实施发布的 ID 令牌省略了 iss(发布者)声明值中所需的 https:// 方案前缀。因此,希望与 Google 合作的依赖方实现将需要有代码来解决这个问题,直到他们的实现被更新。任何此类解决方法代码都应以不会中断的方式编写,谷歌将缺少的前缀添加到其发行者值中。

该文件的日期为 2014 年 11 月 8 日。从那时起,Google 是否已对某个iss值进行了标准化,还是我真的需要同时检查这两个值?上面的评论似乎表明只有 Play Services >=8.3 可以iss使用https://,而其他任何地方的值都是 just accounts.google.com。真的吗?

4

2 回答 2

1

你必须检查这两种可能性。这对我有用...

解码令牌以获取发行者。如果发行人不等于其中之一,https://accounts.google.com或者accounts.google.com您可以停在那里。这是一个无效的令牌。

如果颁发者等于上述任一 Google 字符串,则将相同的解码颁发者值传递给验证步骤。

以下是我用 JavaScript 为一些 Node.js Express 中间件编写的实现:

function authorize(req, res, next) {
    try {
        var token       = req.headers.authorization;
        var decoded     = jwt.decode(token, { complete: true });
        var keyID       = decoded.header.kid;
        var algorithm   = decoded.header.alg;
        var pem         = getPem(keyID);
        var iss         = decoded.payload.iss;

        if (iss === 'accounts.google.com' || iss === 'https://accounts.google.com') {
            var options = {
                audience: CLIENT_ID,
                issuer: iss,
                algorithms: [algorithm]
            }

            jwt.verify(token, pem, options, function(err) {
                if (err) {
                    res.writeHead(401);
                    res.end();
                } else {
                    next();
                }
            });            

        } else {
            res.writeHead(401);
            res.end();
        }
    } catch (err) {
        res.writeHead(401);
        res.end();
    }
}

注意这个函数使用jsonwebtokenjwk-to-pem节点模块。getPem我省略了最终将 json 网络密钥转换为 pem 格式的函数的细节。

于 2017-01-25T07:04:19.307 回答
0

首先,我绝对同意 Google 的文档是一项晦涩难懂的业务。

有几种不同的方法可以验证服务器端 ID 令牌的完整性(顺便说一句,这是您要查找的页面):

  1. “手动” - 不断下载谷歌的公钥,验证签名,然后是每个字段,包括一个字段iss;我在这里看到的主要优势(尽管在我看来很小)是您可以最大限度地减少发送给 Google 的请求数量)。
  2. “自动” - 在 Google 的端点上执行 GET 以验证此令牌 - 迄今为止最简单的: https://www.googleapis.com/oauth2/v3/tokeninfo?id_token={0}
  3. 使用 Google API 客户端库 - 开销可能不值得,C#没有官方的等等。

我建议您使用第二个选项,让 Google 担心验证算法。

于 2016-07-28T06:50:47.370 回答