我可以为 Google OpenIDConnect id_token获得一致的'iss'值吗?



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

一个页面上写着"iss:总是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.

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

为了进一步搅乱局面,OpenIDConnect规范本身包含一个注释:

实现者可能希望知道,截至本文撰写之时,谷歌部署的OpenID Connect实现发布的ID令牌从iss(发布者)声明值中省略了所需的https://scheme前缀。因此,希望与谷歌合作的依赖方实现需要有代码来解决这一问题,直到它们的实现更新为止。任何这样的变通方法代码都应该以一种不会在这种情况下中断的方式编写——谷歌将缺失的前缀添加到它们的发布者值中。

该文件日期为2014年11月8日。从那以后,谷歌是否对iss值进行了标准化,或者我真的需要检查两者吗?上面的注释似乎表明,只有Play Services>=8.3才能获得isshttps://,而在其他任何地方,该值都将仅为accounts.google.com。这是真的吗?

您必须检查这两种可能性。这就是对我有效的…

解码令牌以获取颁发者。如果颁发者不等于https://accounts.google.comaccounts.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 web密钥转换为pem格式。

首先,我肯定同意谷歌的文档是一个模糊的业务。

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

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

我建议你选择第二个选项,让谷歌担心验证算法。

最新更新